commit 60bd1a503fd5b4132d3d203914f3466285c263c1 Author: <> Date: Tue Oct 15 08:46:03 2024 +0000 Deployed ccf00b7 with MkDocs version: 1.6.1 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..6e86b627 --- /dev/null +++ b/404.html @@ -0,0 +1,1462 @@ + + + + + + + + + + + + + + + + + + + + + + + MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.525ec568.min.js b/assets/javascripts/bundle.525ec568.min.js new file mode 100644 index 00000000..4b08eae4 --- /dev/null +++ b/assets/javascripts/bundle.525ec568.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Wi=Object.create;var gr=Object.defineProperty;var Di=Object.getOwnPropertyDescriptor;var Vi=Object.getOwnPropertyNames,Vt=Object.getOwnPropertySymbols,Ni=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,ao=Object.prototype.propertyIsEnumerable;var io=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,$=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&io(e,r,t[r]);if(Vt)for(var r of Vt(t))ao.call(t,r)&&io(e,r,t[r]);return e};var so=(e,t)=>{var r={};for(var o in e)yr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Vt)for(var o of Vt(e))t.indexOf(o)<0&&ao.call(e,o)&&(r[o]=e[o]);return r};var xr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var zi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Vi(t))!yr.call(e,n)&&n!==r&&gr(e,n,{get:()=>t[n],enumerable:!(o=Di(t,n))||o.enumerable});return e};var Mt=(e,t,r)=>(r=e!=null?Wi(Ni(e)):{},zi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var co=(e,t,r)=>new Promise((o,n)=>{var i=p=>{try{s(r.next(p))}catch(c){n(c)}},a=p=>{try{s(r.throw(p))}catch(c){n(c)}},s=p=>p.done?o(p.value):Promise.resolve(p.value).then(i,a);s((r=r.apply(e,t)).next())});var lo=xr((Er,po)=>{(function(e,t){typeof Er=="object"&&typeof po!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(Er,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function p(k){var ft=k.type,qe=k.tagName;return!!(qe==="INPUT"&&a[ft]&&!k.readOnly||qe==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function c(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(s(r.activeElement)&&c(r.activeElement),o=!0)}function u(k){o=!1}function d(k){s(k.target)&&(o||p(k.target))&&c(k.target)}function y(k){s(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function L(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",L,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",y,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var qr=xr((hy,On)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var $a=/["'&<>]/;On.exports=Pa;function Pa(e){var t=""+e,r=$a.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof It=="object"&&typeof Yr=="object"?Yr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof It=="object"?It.ClipboardJS=r():t.ClipboardJS=r()})(It,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Ui}});var a=i(279),s=i.n(a),p=i(370),c=i.n(p),l=i(817),f=i.n(l);function u(V){try{return document.execCommand(V)}catch(A){return!1}}var d=function(A){var M=f()(A);return u("cut"),M},y=d;function L(V){var A=document.documentElement.getAttribute("dir")==="rtl",M=document.createElement("textarea");M.style.fontSize="12pt",M.style.border="0",M.style.padding="0",M.style.margin="0",M.style.position="absolute",M.style[A?"right":"left"]="-9999px";var F=window.pageYOffset||document.documentElement.scrollTop;return M.style.top="".concat(F,"px"),M.setAttribute("readonly",""),M.value=V,M}var X=function(A,M){var F=L(A);M.container.appendChild(F);var D=f()(F);return u("copy"),F.remove(),D},te=function(A){var M=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},F="";return typeof A=="string"?F=X(A,M):A instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(A==null?void 0:A.type)?F=X(A.value,M):(F=f()(A),u("copy")),F},J=te;function k(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(M){return typeof M}:k=function(M){return M&&typeof Symbol=="function"&&M.constructor===Symbol&&M!==Symbol.prototype?"symbol":typeof M},k(V)}var ft=function(){var A=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},M=A.action,F=M===void 0?"copy":M,D=A.container,Y=A.target,$e=A.text;if(F!=="copy"&&F!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Y!==void 0)if(Y&&k(Y)==="object"&&Y.nodeType===1){if(F==="copy"&&Y.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(F==="cut"&&(Y.hasAttribute("readonly")||Y.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if($e)return J($e,{container:D});if(Y)return F==="cut"?y(Y):J(Y,{container:D})},qe=ft;function Fe(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Fe=function(M){return typeof M}:Fe=function(M){return M&&typeof Symbol=="function"&&M.constructor===Symbol&&M!==Symbol.prototype?"symbol":typeof M},Fe(V)}function ki(V,A){if(!(V instanceof A))throw new TypeError("Cannot call a class as a function")}function no(V,A){for(var M=0;M0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof D.action=="function"?D.action:this.defaultAction,this.target=typeof D.target=="function"?D.target:this.defaultTarget,this.text=typeof D.text=="function"?D.text:this.defaultText,this.container=Fe(D.container)==="object"?D.container:document.body}},{key:"listenClick",value:function(D){var Y=this;this.listener=c()(D,"click",function($e){return Y.onClick($e)})}},{key:"onClick",value:function(D){var Y=D.delegateTarget||D.currentTarget,$e=this.action(Y)||"copy",Dt=qe({action:$e,container:this.container,target:this.target(Y),text:this.text(Y)});this.emit(Dt?"success":"error",{action:$e,text:Dt,trigger:Y,clearSelection:function(){Y&&Y.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(D){return vr("action",D)}},{key:"defaultTarget",value:function(D){var Y=vr("target",D);if(Y)return document.querySelector(Y)}},{key:"defaultText",value:function(D){return vr("text",D)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(D){var Y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(D,Y)}},{key:"cut",value:function(D){return y(D)}},{key:"isSupported",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Y=typeof D=="string"?[D]:D,$e=!!document.queryCommandSupported;return Y.forEach(function(Dt){$e=$e&&!!document.queryCommandSupported(Dt)}),$e}}]),M}(s()),Ui=Fi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,p){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(p))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(l,f,u,d,y){var L=c.apply(this,arguments);return l.addEventListener(u,L,y),{destroy:function(){l.removeEventListener(u,L,y)}}}function p(l,f,u,d,y){return typeof l.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(L){return s(L,f,u,d,y)}))}function c(l,f,u,d){return function(y){y.delegateTarget=a(y.target,f),y.delegateTarget&&d.call(l,y)}}o.exports=p},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function p(u,d,y){if(!u&&!d&&!y)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(y))throw new TypeError("Third argument must be a Function");if(a.node(u))return c(u,d,y);if(a.nodeList(u))return l(u,d,y);if(a.string(u))return f(u,d,y);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(u,d,y){return u.addEventListener(d,y),{destroy:function(){u.removeEventListener(d,y)}}}function l(u,d,y){return Array.prototype.forEach.call(u,function(L){L.addEventListener(d,y)}),{destroy:function(){Array.prototype.forEach.call(u,function(L){L.removeEventListener(d,y)})}}}function f(u,d,y){return s(document.body,u,d,y)}o.exports=p},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var p=window.getSelection(),c=document.createRange();c.selectNodeContents(i),p.removeAllRanges(),p.addRange(c),a=p.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var p=this.e||(this.e={});return(p[i]||(p[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var p=this;function c(){p.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),p=0,c=s.length;for(p;p0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function N(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],a;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(s){a={error:s}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(a)throw a.error}}return i}function q(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||p(d,L)})},y&&(n[d]=y(n[d])))}function p(d,y){try{c(o[d](y))}catch(L){u(i[0][3],L)}}function c(d){d.value instanceof nt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){p("next",d)}function f(d){p("throw",d)}function u(d,y){d(y),i.shift(),i.length&&p(i[0][0],i[0][1])}}function uo(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof he=="function"?he(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(a){return new Promise(function(s,p){a=e[i](a),n(s,p,a.done,a.value)})}}function n(i,a,s,p){Promise.resolve(p).then(function(c){i({value:c,done:s})},a)}}function H(e){return typeof e=="function"}function ut(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var zt=ut(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Qe(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ue=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=he(a),p=s.next();!p.done;p=s.next()){var c=p.value;c.remove(this)}}catch(L){t={error:L}}finally{try{p&&!p.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var l=this.initialTeardown;if(H(l))try{l()}catch(L){i=L instanceof zt?L.errors:[L]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=he(f),d=u.next();!d.done;d=u.next()){var y=d.value;try{ho(y)}catch(L){i=i!=null?i:[],L instanceof zt?i=q(q([],N(i)),N(L.errors)):i.push(L)}}}catch(L){o={error:L}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new zt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ho(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Qe(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Qe(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Tr=Ue.EMPTY;function qt(e){return e instanceof Ue||e&&"closed"in e&&H(e.remove)&&H(e.add)&&H(e.unsubscribe)}function ho(e){H(e)?e():e.unsubscribe()}var Pe={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var dt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,a=n.isStopped,s=n.observers;return i||a?Tr:(this.currentObservers=null,s.push(r),new Ue(function(){o.currentObservers=null,Qe(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new To(r,o)},t}(j);var To=function(e){oe(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Tr},t}(g);var _r=function(e){oe(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(g);var At={now:function(){return(At.delegate||Date).now()},delegate:void 0};var Ct=function(e){oe(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=At);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,p=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+p)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),p=0;p0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(gt);var Lo=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(yt);var kr=new Lo(Oo);var Mo=function(e){oe(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=vt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var a=r.actions;o!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==o&&(vt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(gt);var _o=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(yt);var me=new _o(Mo);var S=new j(function(e){return e.complete()});function Yt(e){return e&&H(e.schedule)}function Hr(e){return e[e.length-1]}function Xe(e){return H(Hr(e))?e.pop():void 0}function ke(e){return Yt(Hr(e))?e.pop():void 0}function Bt(e,t){return typeof Hr(e)=="number"?e.pop():t}var xt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Gt(e){return H(e==null?void 0:e.then)}function Jt(e){return H(e[bt])}function Xt(e){return Symbol.asyncIterator&&H(e==null?void 0:e[Symbol.asyncIterator])}function Zt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var er=Zi();function tr(e){return H(e==null?void 0:e[er])}function rr(e){return fo(this,arguments,function(){var r,o,n,i;return Nt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,nt(r.read())];case 3:return o=a.sent(),n=o.value,i=o.done,i?[4,nt(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,nt(n)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function or(e){return H(e==null?void 0:e.getReader)}function U(e){if(e instanceof j)return e;if(e!=null){if(Jt(e))return ea(e);if(xt(e))return ta(e);if(Gt(e))return ra(e);if(Xt(e))return Ao(e);if(tr(e))return oa(e);if(or(e))return na(e)}throw Zt(e)}function ea(e){return new j(function(t){var r=e[bt]();if(H(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function ta(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?b(function(n,i){return e(n,i,o)}):le,Te(1),r?De(t):Qo(function(){return new ir}))}}function jr(e){return e<=0?function(){return S}:E(function(t,r){var o=[];t.subscribe(T(r,function(n){o.push(n),e=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new g}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,p=s===void 0?!0:s;return function(c){var l,f,u,d=0,y=!1,L=!1,X=function(){f==null||f.unsubscribe(),f=void 0},te=function(){X(),l=u=void 0,y=L=!1},J=function(){var k=l;te(),k==null||k.unsubscribe()};return E(function(k,ft){d++,!L&&!y&&X();var qe=u=u!=null?u:r();ft.add(function(){d--,d===0&&!L&&!y&&(f=Ur(J,p))}),qe.subscribe(ft),!l&&d>0&&(l=new at({next:function(Fe){return qe.next(Fe)},error:function(Fe){L=!0,X(),f=Ur(te,n,Fe),qe.error(Fe)},complete:function(){y=!0,X(),f=Ur(te,a),qe.complete()}}),U(k).subscribe(l))})(c)}}function Ur(e,t){for(var r=[],o=2;oe.next(document)),e}function P(e,t=document){return Array.from(t.querySelectorAll(e))}function R(e,t=document){let r=fe(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function fe(e,t=document){return t.querySelector(e)||void 0}function Ie(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var wa=O(h(document.body,"focusin"),h(document.body,"focusout")).pipe(_e(1),Q(void 0),m(()=>Ie()||document.body),G(1));function et(e){return wa.pipe(m(t=>e.contains(t)),K())}function $t(e,t){return C(()=>O(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?Ht(r=>Le(+!r*t)):le,Q(e.matches(":hover"))))}function Jo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Jo(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Jo(o,n);return o}function sr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function Tt(e){let t=x("script",{src:e});return C(()=>(document.head.appendChild(t),O(h(t,"load"),h(t,"error").pipe(v(()=>$r(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),Te(1))))}var Xo=new g,Ta=C(()=>typeof ResizeObserver=="undefined"?Tt("https://unpkg.com/resize-observer-polyfill"):I(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>Xo.next(t)))),v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function ce(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ta.pipe(w(r=>r.observe(t)),v(r=>Xo.pipe(b(o=>o.target===t),_(()=>r.unobserve(t)))),m(()=>ce(e)),Q(ce(e)))}function St(e){return{width:e.scrollWidth,height:e.scrollHeight}}function cr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function Zo(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Ve(e){return{x:e.offsetLeft,y:e.offsetTop}}function en(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function tn(e){return O(h(window,"load"),h(window,"resize")).pipe(Me(0,me),m(()=>Ve(e)),Q(Ve(e)))}function pr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ne(e){return O(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe(Me(0,me),m(()=>pr(e)),Q(pr(e)))}var rn=new g,Sa=C(()=>I(new IntersectionObserver(e=>{for(let t of e)rn.next(t)},{threshold:0}))).pipe(v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function tt(e){return Sa.pipe(w(t=>t.observe(e)),v(t=>rn.pipe(b(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function on(e,t=16){return Ne(e).pipe(m(({y:r})=>{let o=ce(e),n=St(e);return r>=n.height-o.height-t}),K())}var lr={drawer:R("[data-md-toggle=drawer]"),search:R("[data-md-toggle=search]")};function nn(e){return lr[e].checked}function Je(e,t){lr[e].checked!==t&&lr[e].click()}function ze(e){let t=lr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function Oa(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function La(){return O(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function an(){let e=h(window,"keydown").pipe(b(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:nn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),b(({mode:t,type:r})=>{if(t==="global"){let o=Ie();if(typeof o!="undefined")return!Oa(o,r)}return!0}),pe());return La().pipe(v(t=>t?S:e))}function ye(){return new URL(location.href)}function lt(e,t=!1){if(B("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function sn(){return new g}function cn(){return location.hash.slice(1)}function pn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Ma(e){return O(h(window,"hashchange"),e).pipe(m(cn),Q(cn()),b(t=>t.length>0),G(1))}function ln(e){return Ma(e).pipe(m(t=>fe(`[id="${t}"]`)),b(t=>typeof t!="undefined"))}function Pt(e){let t=matchMedia(e);return ar(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function mn(){let e=matchMedia("print");return O(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function Nr(e,t){return e.pipe(v(r=>r?t():S))}function zr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let a=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+a*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function je(e,t){return zr(e,t).pipe(v(r=>r.text()),m(r=>JSON.parse(r)),G(1))}function fn(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),G(1))}function un(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),G(1))}function dn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function hn(){return O(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(dn),Q(dn()))}function bn(){return{width:innerWidth,height:innerHeight}}function vn(){return h(window,"resize",{passive:!0}).pipe(m(bn),Q(bn()))}function gn(){return z([hn(),vn()]).pipe(m(([e,t])=>({offset:e,size:t})),G(1))}function mr(e,{viewport$:t,header$:r}){let o=t.pipe(ee("size")),n=z([o,r]).pipe(m(()=>Ve(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:a,size:s},{x:p,y:c}])=>({offset:{x:a.x-p,y:a.y-c+i},size:s})))}function _a(e){return h(e,"message",t=>t.data)}function Aa(e){let t=new g;return t.subscribe(r=>e.postMessage(r)),t}function yn(e,t=new Worker(e)){let r=_a(t),o=Aa(t),n=new g;n.subscribe(o);let i=o.pipe(Z(),ie(!0));return n.pipe(Z(),Re(r.pipe(W(i))),pe())}var Ca=R("#__config"),Ot=JSON.parse(Ca.textContent);Ot.base=`${new URL(Ot.base,ye())}`;function xe(){return Ot}function B(e){return Ot.features.includes(e)}function Ee(e,t){return typeof t!="undefined"?Ot.translations[e].replace("#",t.toString()):Ot.translations[e]}function Se(e,t=document){return R(`[data-md-component=${e}]`,t)}function ae(e,t=document){return P(`[data-md-component=${e}]`,t)}function ka(e){let t=R(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>R(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function xn(e){if(!B("announce.dismiss")||!e.childElementCount)return S;if(!e.hidden){let t=R(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return C(()=>{let t=new g;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),ka(e).pipe(w(r=>t.next(r)),_(()=>t.complete()),m(r=>$({ref:e},r)))})}function Ha(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function En(e,t){let r=new g;return r.subscribe(({hidden:o})=>{e.hidden=o}),Ha(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))}function Rt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Tn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Sn(e){return x("button",{class:"md-clipboard md-icon",title:Ee("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}var Ln=Mt(qr());function Qr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(p=>!e.terms[p]).reduce((p,c)=>[...p,x("del",null,(0,Ln.default)(c))," "],[]).slice(0,-1),i=xe(),a=new URL(e.location,i.base);B("search.highlight")&&a.searchParams.set("h",Object.entries(e.terms).filter(([,p])=>p).reduce((p,[c])=>`${p} ${c}`.trim(),""));let{tags:s}=xe();return x("a",{href:`${a}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(p=>{let c=s?p in s?`md-tag-icon md-tag--${s[p]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${c}`},p)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Ee("search.result.term.missing"),": ",...n)))}function Mn(e){let t=e[0].score,r=[...e],o=xe(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),a=r.findIndex(l=>l.scoreQr(l,1)),...p.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,p.length>0&&p.length===1?Ee("search.result.more.one"):Ee("search.result.more.other",p.length))),...p.map(l=>Qr(l,1)))]:[]];return x("li",{class:"md-search-result__item"},c)}function _n(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?sr(r):r)))}function Kr(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function An(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Ra(e){var o;let t=xe(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Cn(e,t){var o;let r=xe();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Ee("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Ra)))}var Ia=0;function ja(e){let t=z([et(e),$t(e)]).pipe(m(([o,n])=>o||n),K()),r=C(()=>Zo(e)).pipe(ne(Ne),pt(1),He(t),m(()=>en(e)));return t.pipe(Ae(o=>o),v(()=>z([t,r])),m(([o,n])=>({active:o,offset:n})),pe())}function Fa(e,t){let{content$:r,viewport$:o}=t,n=`__tooltip2_${Ia++}`;return C(()=>{let i=new g,a=new _r(!1);i.pipe(Z(),ie(!1)).subscribe(a);let s=a.pipe(Ht(c=>Le(+!c*250,kr)),K(),v(c=>c?r:S),w(c=>c.id=n),pe());z([i.pipe(m(({active:c})=>c)),s.pipe(v(c=>$t(c,250)),Q(!1))]).pipe(m(c=>c.some(l=>l))).subscribe(a);let p=a.pipe(b(c=>c),re(s,o),m(([c,l,{size:f}])=>{let u=e.getBoundingClientRect(),d=u.width/2;if(l.role==="tooltip")return{x:d,y:8+u.height};if(u.y>=f.height/2){let{height:y}=ce(l);return{x:d,y:-16-y}}else return{x:d,y:16+u.height}}));return z([s,i,p]).subscribe(([c,{offset:l},f])=>{c.style.setProperty("--md-tooltip-host-x",`${l.x}px`),c.style.setProperty("--md-tooltip-host-y",`${l.y}px`),c.style.setProperty("--md-tooltip-x",`${f.x}px`),c.style.setProperty("--md-tooltip-y",`${f.y}px`),c.classList.toggle("md-tooltip2--top",f.y<0),c.classList.toggle("md-tooltip2--bottom",f.y>=0)}),a.pipe(b(c=>c),re(s,(c,l)=>l),b(c=>c.role==="tooltip")).subscribe(c=>{let l=ce(R(":scope > *",c));c.style.setProperty("--md-tooltip-width",`${l.width}px`),c.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(K(),ve(me),re(s)).subscribe(([c,l])=>{l.classList.toggle("md-tooltip2--active",c)}),z([a.pipe(b(c=>c)),s]).subscribe(([c,l])=>{l.role==="dialog"?(e.setAttribute("aria-controls",n),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",n)}),a.pipe(b(c=>!c)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),ja(e).pipe(w(c=>i.next(c)),_(()=>i.complete()),m(c=>$({ref:e},c)))})}function mt(e,{viewport$:t},r=document.body){return Fa(e,{content$:new j(o=>{let n=e.title,i=wn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t})}function Ua(e,t){let r=C(()=>z([tn(e),Ne(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:a,height:s}=ce(e);return{x:o-i.x+a/2,y:n-i.y+s/2}}));return et(e).pipe(v(o=>r.pipe(m(n=>({active:o,offset:n})),Te(+!o||1/0))))}function kn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return C(()=>{let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({offset:s}){e.style.setProperty("--md-tooltip-x",`${s.x}px`),e.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),tt(e).pipe(W(a)).subscribe(s=>{e.toggleAttribute("data-md-visible",s)}),O(i.pipe(b(({active:s})=>s)),i.pipe(_e(250),b(({active:s})=>!s))).subscribe({next({active:s}){s?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Me(16,me)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?e.style.setProperty("--md-tooltip-0",`${-s}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(a),b(s=>!(s.metaKey||s.ctrlKey))).subscribe(s=>{s.stopPropagation(),s.preventDefault()}),h(n,"mousedown").pipe(W(a),re(i)).subscribe(([s,{active:p}])=>{var c;if(s.button!==0||s.metaKey||s.ctrlKey)s.preventDefault();else if(p){s.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(c=Ie())==null||c.blur()}}),r.pipe(W(a),b(s=>s===o),Ge(125)).subscribe(()=>e.focus()),Ua(e,t).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function Wa(e){return e.tagName==="CODE"?P(".c, .c1, .cm",e):[e]}function Da(e){let t=[];for(let r of Wa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let a;for(;a=/(\(\d+\))(!)?/.exec(i.textContent);){let[,s,p]=a;if(typeof p=="undefined"){let c=i.splitText(a.index);i=c.splitText(s.length),t.push(c)}else{i.textContent=s,t.push(i);break}}}}return t}function Hn(e,t){t.append(...Array.from(e.childNodes))}function fr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,a=new Map;for(let s of Da(t)){let[,p]=s.textContent.match(/\((\d+)\)/);fe(`:scope > li:nth-child(${p})`,e)&&(a.set(p,Tn(p,i)),s.replaceWith(a.get(p)))}return a.size===0?S:C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=[];for(let[l,f]of a)c.push([R(".md-typeset",f),R(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(p)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of c)l?Hn(f,u):Hn(u,f)}),O(...[...a].map(([,l])=>kn(l,t,{target$:r}))).pipe(_(()=>s.complete()),pe())})}function $n(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return $n(t)}}function Pn(e,t){return C(()=>{let r=$n(e);return typeof r!="undefined"?fr(r,e,t):S})}var Rn=Mt(Br());var Va=0;function In(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return In(t)}}function Na(e){return ge(e).pipe(m(({width:t})=>({scrollable:St(e).width>t})),ee("scrollable"))}function jn(e,t){let{matches:r}=matchMedia("(hover)"),o=C(()=>{let n=new g,i=n.pipe(jr(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let a=[];if(Rn.default.isSupported()&&(e.closest(".copy")||B("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${Va++}`;let l=Sn(c.id);c.insertBefore(l,e),B("content.tooltips")&&a.push(mt(l,{viewport$}))}let s=e.closest(".highlight");if(s instanceof HTMLElement){let c=In(s);if(typeof c!="undefined"&&(s.classList.contains("annotate")||B("content.code.annotate"))){let l=fr(c,e,t);a.push(ge(s).pipe(W(i),m(({width:f,height:u})=>f&&u),K(),v(f=>f?l:S)))}}return P(":scope > span[id]",e).length&&e.classList.add("md-code__content"),Na(e).pipe(w(c=>n.next(c)),_(()=>n.complete()),m(c=>$({ref:e},c)),Re(...a))});return B("content.lazy")?tt(e).pipe(b(n=>n),Te(1),v(()=>o)):o}function za(e,{target$:t,print$:r}){let o=!0;return O(t.pipe(m(n=>n.closest("details:not([open])")),b(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(b(n=>n||!o),w(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Fn(e,t){return C(()=>{let r=new g;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),za(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}var Un=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var Gr,Qa=0;function Ka(){return typeof mermaid=="undefined"||mermaid instanceof Element?Tt("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):I(void 0)}function Wn(e){return e.classList.remove("mermaid"),Gr||(Gr=Ka().pipe(w(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Un,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),G(1))),Gr.subscribe(()=>co(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${Qa++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),a=r.attachShadow({mode:"closed"});a.innerHTML=n,e.replaceWith(r),i==null||i(a)})),Gr.pipe(m(()=>({ref:e})))}var Dn=x("table");function Vn(e){return e.replaceWith(Dn),Dn.replaceWith(An(e)),I({ref:e})}function Ya(e){let t=e.find(r=>r.checked)||e[0];return O(...e.map(r=>h(r,"change").pipe(m(()=>R(`label[for="${r.id}"]`))))).pipe(Q(R(`label[for="${t.id}"]`)),m(r=>({active:r})))}function Nn(e,{viewport$:t,target$:r}){let o=R(".tabbed-labels",e),n=P(":scope > input",e),i=Kr("prev");e.append(i);let a=Kr("next");return e.append(a),C(()=>{let s=new g,p=s.pipe(Z(),ie(!0));z([s,ge(e),tt(e)]).pipe(W(p),Me(1,me)).subscribe({next([{active:c},l]){let f=Ve(c),{width:u}=ce(c);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=pr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ne(o),ge(o)]).pipe(W(p)).subscribe(([c,l])=>{let f=St(o);i.hidden=c.x<16,a.hidden=c.x>f.width-l.width-16}),O(h(i,"click").pipe(m(()=>-1)),h(a,"click").pipe(m(()=>1))).pipe(W(p)).subscribe(c=>{let{width:l}=ce(o);o.scrollBy({left:l*c,behavior:"smooth"})}),r.pipe(W(p),b(c=>n.includes(c))).subscribe(c=>c.click()),o.classList.add("tabbed-labels--linked");for(let c of n){let l=R(`label[for="${c.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(p),b(f=>!(f.metaKey||f.ctrlKey)),w(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return B("content.tabs.link")&&s.pipe(Ce(1),re(t)).subscribe(([{active:c},{offset:l}])=>{let f=c.innerText.trim();if(c.hasAttribute("data-md-switching"))c.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let y of P("[data-tabs]"))for(let L of P(":scope > input",y)){let X=R(`label[for="${L.id}"]`);if(X!==c&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),L.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),s.pipe(W(p)).subscribe(()=>{for(let c of P("audio, video",e))c.pause()}),Ya(n).pipe(w(c=>s.next(c)),_(()=>s.complete()),m(c=>$({ref:e},c)))}).pipe(Ke(se))}function zn(e,{viewport$:t,target$:r,print$:o}){return O(...P(".annotate:not(.highlight)",e).map(n=>Pn(n,{target$:r,print$:o})),...P("pre:not(.mermaid) > code",e).map(n=>jn(n,{target$:r,print$:o})),...P("pre.mermaid",e).map(n=>Wn(n)),...P("table:not([class])",e).map(n=>Vn(n)),...P("details",e).map(n=>Fn(n,{target$:r,print$:o})),...P("[data-tabs]",e).map(n=>Nn(n,{viewport$:t,target$:r})),...P("[title]",e).filter(()=>B("content.tooltips")).map(n=>mt(n,{viewport$:t})))}function Ba(e,{alert$:t}){return t.pipe(v(r=>O(I(!0),I(!1).pipe(Ge(2e3))).pipe(m(o=>({message:r,active:o})))))}function qn(e,t){let r=R(".md-typeset",e);return C(()=>{let o=new g;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ba(e,t).pipe(w(n=>o.next(n)),_(()=>o.complete()),m(n=>$({ref:e},n)))})}var Ga=0;function Ja(e,t){document.body.append(e);let{width:r}=ce(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=cr(t),n=typeof o!="undefined"?Ne(o):I({x:0,y:0}),i=O(et(t),$t(t)).pipe(K());return z([i,n]).pipe(m(([a,s])=>{let{x:p,y:c}=Ve(t),l=ce(t),f=t.closest("table");return f&&t.parentElement&&(p+=f.offsetLeft+t.parentElement.offsetLeft,c+=f.offsetTop+t.parentElement.offsetTop),{active:a,offset:{x:p-s.x+l.width/2-r/2,y:c-s.y+l.height+8}}}))}function Qn(e){let t=e.title;if(!t.length)return S;let r=`__tooltip_${Ga++}`,o=Rt(r,"inline"),n=R(".md-typeset",o);return n.innerHTML=t,C(()=>{let i=new g;return i.subscribe({next({offset:a}){o.style.setProperty("--md-tooltip-x",`${a.x}px`),o.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),O(i.pipe(b(({active:a})=>a)),i.pipe(_e(250),b(({active:a})=>!a))).subscribe({next({active:a}){a?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Me(16,me)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?o.style.setProperty("--md-tooltip-0",`${-a}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ja(o,e).pipe(w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))}).pipe(Ke(se))}function Xa({viewport$:e}){if(!B("header.autohide"))return I(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Be(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),K()),o=ze("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),K(),v(n=>n?r:I(!1)),Q(!1))}function Kn(e,t){return C(()=>z([ge(e),Xa(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),K((r,o)=>r.height===o.height&&r.hidden===o.hidden),G(1))}function Yn(e,{header$:t,main$:r}){return C(()=>{let o=new g,n=o.pipe(Z(),ie(!0));o.pipe(ee("active"),He(t)).subscribe(([{active:a},{hidden:s}])=>{e.classList.toggle("md-header--shadow",a&&!s),e.hidden=s});let i=ue(P("[title]",e)).pipe(b(()=>B("content.tooltips")),ne(a=>Qn(a)));return r.subscribe(o),t.pipe(W(n),m(a=>$({ref:e},a)),Re(i.pipe(W(n))))})}function Za(e,{viewport$:t,header$:r}){return mr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=ce(e);return{active:o>=n}}),ee("active"))}function Bn(e,t){return C(()=>{let r=new g;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=fe(".md-content h1");return typeof o=="undefined"?S:Za(o,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))})}function Gn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),K()),n=o.pipe(v(()=>ge(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ee("bottom"))));return z([o,n,t]).pipe(m(([i,{top:a,bottom:s},{offset:{y:p},size:{height:c}}])=>(c=Math.max(0,c-Math.max(0,a-p,i)-Math.max(0,c+p-s)),{offset:a-i,height:c,active:a-i<=p})),K((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function es(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return I(...e).pipe(ne(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),G(1))}function Jn(e){let t=P("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Pt("(prefers-color-scheme: light)");return C(()=>{let i=new g;return i.subscribe(a=>{if(document.body.setAttribute("data-md-color-switching",""),a.color.media==="(prefers-color-scheme)"){let s=matchMedia("(prefers-color-scheme: light)"),p=document.querySelector(s.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");a.color.scheme=p.getAttribute("data-md-color-scheme"),a.color.primary=p.getAttribute("data-md-color-primary"),a.color.accent=p.getAttribute("data-md-color-accent")}for(let[s,p]of Object.entries(a.color))document.body.setAttribute(`data-md-color-${s}`,p);for(let s=0;sa.key==="Enter"),re(i,(a,s)=>s)).subscribe(({index:a})=>{a=(a+1)%t.length,t[a].click(),t[a].focus()}),i.pipe(m(()=>{let a=Se("header"),s=window.getComputedStyle(a);return o.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(p=>(+p).toString(16).padStart(2,"0")).join("")})).subscribe(a=>r.content=`#${a}`),i.pipe(ve(se)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),es(t).pipe(W(n.pipe(Ce(1))),ct(),w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))})}function Xn(e,{progress$:t}){return C(()=>{let r=new g;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(w(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Jr=Mt(Br());function ts(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Zn({alert$:e}){Jr.default.isSupported()&&new j(t=>{new Jr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ts(R(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(w(t=>{t.trigger.focus()}),m(()=>Ee("clipboard.copied"))).subscribe(e)}function ei(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function rs(e,t){let r=new Map;for(let o of P("url",e)){let n=R("loc",o),i=[ei(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let a of P("[rel=alternate]",o)){let s=a.getAttribute("href");s!=null&&i.push(ei(new URL(s),t))}}return r}function ur(e){return un(new URL("sitemap.xml",e)).pipe(m(t=>rs(t,new URL(e))),de(()=>I(new Map)))}function os(e,t){if(!(e.target instanceof Element))return S;let r=e.target.closest("a");if(r===null)return S;if(r.target||e.metaKey||e.ctrlKey)return S;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),I(new URL(r.href))):S}function ti(e){let t=new Map;for(let r of P(":scope > *",e.head))t.set(r.outerHTML,r);return t}function ri(e){for(let t of P("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return I(e)}function ns(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...B("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=fe(o),i=fe(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=ti(document);for(let[o,n]of ti(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Se("container");return We(P("script",r)).pipe(v(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),S}),Z(),ie(document))}function oi({location$:e,viewport$:t,progress$:r}){let o=xe();if(location.protocol==="file:")return S;let n=ur(o.base);I(document).subscribe(ri);let i=h(document.body,"click").pipe(He(n),v(([p,c])=>os(p,c)),pe()),a=h(window,"popstate").pipe(m(ye),pe());i.pipe(re(t)).subscribe(([p,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",p)}),O(i,a).subscribe(e);let s=e.pipe(ee("pathname"),v(p=>fn(p,{progress$:r}).pipe(de(()=>(lt(p,!0),S)))),v(ri),v(ns),pe());return O(s.pipe(re(e,(p,c)=>c)),s.pipe(v(()=>e),ee("pathname"),v(()=>e),ee("hash")),e.pipe(K((p,c)=>p.pathname===c.pathname&&p.hash===c.hash),v(()=>i),w(()=>history.back()))).subscribe(p=>{var c,l;history.state!==null||!p.hash?window.scrollTo(0,(l=(c=history.state)==null?void 0:c.y)!=null?l:0):(history.scrollRestoration="auto",pn(p.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(ee("offset"),_e(100)).subscribe(({offset:p})=>{history.replaceState(p,"")}),s}var ni=Mt(qr());function ii(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(0,ni.default)(a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function jt(e){return e.type===1}function dr(e){return e.type===3}function ai(e,t){let r=yn(e);return O(I(location.protocol!=="file:"),ze("search")).pipe(Ae(o=>o),v(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:B("search.suggest")}}})),r}function si(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=Xr(n))==null?void 0:l.pathname;if(i===void 0)return;let a=ss(o.pathname,i);if(a===void 0)return;let s=ps(t.keys());if(!t.has(s))return;let p=Xr(a,s);if(!p||!t.has(p.href))return;let c=Xr(a,r);if(c)return c.hash=o.hash,c.search=o.search,c}function Xr(e,t){try{return new URL(e,t)}catch(r){return}}function ss(e,t){if(e.startsWith(t))return e.slice(t.length)}function cs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oS)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:a,aliases:s})=>a===i||s.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),v(n=>h(document.body,"click").pipe(b(i=>!i.metaKey&&!i.ctrlKey),re(o),v(([i,a])=>{if(i.target instanceof Element){let s=i.target.closest("a");if(s&&!s.target&&n.has(s.href)){let p=s.href;return!i.target.closest(".md-version")&&n.get(p)===a?S:(i.preventDefault(),I(new URL(p)))}}return S}),v(i=>ur(i).pipe(m(a=>{var s;return(s=si({selectedVersionSitemap:a,selectedVersionBaseURL:i,currentLocation:ye(),currentBaseURL:t.base}))!=null?s:i})))))).subscribe(n=>lt(n,!0)),z([r,o]).subscribe(([n,i])=>{R(".md-header__topic").appendChild(Cn(n,i))}),e.pipe(v(()=>o)).subscribe(n=>{var a;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let s=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(s)||(s=[s]);e:for(let p of s)for(let c of n.aliases.concat(n.version))if(new RegExp(p,"i").test(c)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let s of ae("outdated"))s.hidden=!1})}function ls(e,{worker$:t}){let{searchParams:r}=ye();r.has("q")&&(Je("search",!0),e.value=r.get("q"),e.focus(),ze("search").pipe(Ae(i=>!i)).subscribe(()=>{let i=ye();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=et(e),n=O(t.pipe(Ae(jt)),h(e,"keyup"),o).pipe(m(()=>e.value),K());return z([n,o]).pipe(m(([i,a])=>({value:i,focus:a})),G(1))}function pi(e,{worker$:t}){let r=new g,o=r.pipe(Z(),ie(!0));z([t.pipe(Ae(jt)),r],(i,a)=>a).pipe(ee("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ee("focus")).subscribe(({focus:i})=>{i&&Je("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=R("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ls(e,{worker$:t}).pipe(w(i=>r.next(i)),_(()=>r.complete()),m(i=>$({ref:e},i)),G(1))}function li(e,{worker$:t,query$:r}){let o=new g,n=on(e.parentElement).pipe(b(Boolean)),i=e.parentElement,a=R(":scope > :first-child",e),s=R(":scope > :last-child",e);ze("search").subscribe(l=>s.setAttribute("role",l?"list":"presentation")),o.pipe(re(r),Wr(t.pipe(Ae(jt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:a.textContent=f.length?Ee("search.result.none"):Ee("search.result.placeholder");break;case 1:a.textContent=Ee("search.result.one");break;default:let u=sr(l.length);a.textContent=Ee("search.result.other",u)}});let p=o.pipe(w(()=>s.innerHTML=""),v(({items:l})=>O(I(...l.slice(0,10)),I(...l.slice(10)).pipe(Be(4),Vr(n),v(([f])=>f)))),m(Mn),pe());return p.subscribe(l=>s.appendChild(l)),p.pipe(ne(l=>{let f=fe("details",l);return typeof f=="undefined"?S:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(b(dr),m(({data:l})=>l)).pipe(w(l=>o.next(l)),_(()=>o.complete()),m(l=>$({ref:e},l)))}function ms(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ye();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function mi(e,t){let r=new g,o=r.pipe(Z(),ie(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),ms(e,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))}function fi(e,{worker$:t,keyboard$:r}){let o=new g,n=Se("search-query"),i=O(h(n,"keydown"),h(n,"focus")).pipe(ve(se),m(()=>n.value),K());return o.pipe(He(i),m(([{suggest:s},p])=>{let c=p.split(/([\s-]+)/);if(s!=null&&s.length&&c[c.length-1]){let l=s[s.length-1];l.startsWith(c[c.length-1])&&(c[c.length-1]=l)}else c.length=0;return c})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(b(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(b(dr),m(({data:s})=>s)).pipe(w(s=>o.next(s)),_(()=>o.complete()),m(()=>({ref:e})))}function ui(e,{index$:t,keyboard$:r}){let o=xe();try{let n=ai(o.search,t),i=Se("search-query",e),a=Se("search-result",e);h(e,"click").pipe(b(({target:p})=>p instanceof Element&&!!p.closest("a"))).subscribe(()=>Je("search",!1)),r.pipe(b(({mode:p})=>p==="search")).subscribe(p=>{let c=Ie();switch(p.type){case"Enter":if(c===i){let l=new Map;for(let f of P(":first-child [href]",a)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}p.claim()}break;case"Escape":case"Tab":Je("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof c=="undefined")i.focus();else{let l=[i,...P(":not(details) > [href], summary, details[open] [href]",a)],f=Math.max(0,(Math.max(0,l.indexOf(c))+l.length+(p.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}p.claim();break;default:i!==Ie()&&i.focus()}}),r.pipe(b(({mode:p})=>p==="global")).subscribe(p=>{switch(p.type){case"f":case"s":case"/":i.focus(),i.select(),p.claim();break}});let s=pi(i,{worker$:n});return O(s,li(a,{worker$:n,query$:s})).pipe(Re(...ae("search-share",e).map(p=>mi(p,{query$:s})),...ae("search-suggest",e).map(p=>fi(p,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ye}}function di(e,{index$:t,location$:r}){return z([t,r.pipe(Q(ye()),b(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>ii(o.config)(n.searchParams.get("h"))),m(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)!=null&&a.offsetHeight){let p=s.textContent,c=o(p);c.length>p.length&&n.set(s,c)}for(let[s,p]of n){let{childNodes:c}=x("span",null,p);s.replaceWith(...Array.from(c))}return{ref:e,nodes:n}}))}function fs(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:a},{offset:{y:s}}])=>(a=a+Math.min(n,Math.max(0,s-i))-n,{height:a,locked:s>=i+n})),K((i,a)=>i.height===a.height&&i.locked===a.locked))}function Zr(e,o){var n=o,{header$:t}=n,r=so(n,["header$"]);let i=R(".md-sidebar__scrollwrap",e),{y:a}=Ve(i);return C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=s.pipe(Me(0,me));return c.pipe(re(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*a}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),c.pipe(Ae()).subscribe(()=>{for(let l of P(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2})}}}),ue(P("label[tabindex]",e)).pipe(ne(l=>h(l,"click").pipe(ve(se),m(()=>l),W(p)))).subscribe(l=>{let f=R(`[id="${l.htmlFor}"]`);R(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),fs(e,r).pipe(w(l=>s.next(l)),_(()=>s.complete()),m(l=>$({ref:e},l)))})}function hi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return st(je(`${r}/releases/latest`).pipe(de(()=>S),m(o=>({version:o.tag_name})),De({})),je(r).pipe(de(()=>S),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return je(r).pipe(m(o=>({repositories:o.public_repos})),De({}))}}function bi(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return st(je(`${r}/releases/permalink/latest`).pipe(de(()=>S),m(({tag_name:o})=>({version:o})),De({})),je(r).pipe(de(()=>S),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}function vi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return hi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return bi(r,o)}return S}var us;function ds(e){return us||(us=C(()=>{let t=__md_get("__source",sessionStorage);if(t)return I(t);if(ae("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return S}return vi(e.href).pipe(w(o=>__md_set("__source",o,sessionStorage)))}).pipe(de(()=>S),b(t=>Object.keys(t).length>0),m(t=>({facts:t})),G(1)))}function gi(e){let t=R(":scope > :last-child",e);return C(()=>{let r=new g;return r.subscribe(({facts:o})=>{t.appendChild(_n(o)),t.classList.add("md-source__repository--active")}),ds(e).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function hs(e,{viewport$:t,header$:r}){return ge(document.body).pipe(v(()=>mr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ee("hidden"))}function yi(e,t){return C(()=>{let r=new g;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(B("navigation.tabs.sticky")?I({hidden:!1}):hs(e,t)).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function bs(e,{viewport$:t,header$:r}){let o=new Map,n=P(".md-nav__link",e);for(let s of n){let p=decodeURIComponent(s.hash.substring(1)),c=fe(`[id="${p}"]`);typeof c!="undefined"&&o.set(s,c)}let i=r.pipe(ee("height"),m(({height:s})=>{let p=Se("main"),c=R(":scope > :first-child",p);return s+.8*(c.offsetTop-p.offsetTop)}),pe());return ge(document.body).pipe(ee("height"),v(s=>C(()=>{let p=[];return I([...o].reduce((c,[l,f])=>{for(;p.length&&o.get(p[p.length-1]).tagName>=f.tagName;)p.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return c.set([...p=[...p,l]].reverse(),u)},new Map))}).pipe(m(p=>new Map([...p].sort(([,c],[,l])=>c-l))),He(i),v(([p,c])=>t.pipe(Fr(([l,f],{offset:{y:u},size:d})=>{let y=u+d.height>=Math.floor(s.height);for(;f.length;){let[,L]=f[0];if(L-c=u&&!y)f=[l.pop(),...f];else break}return[l,f]},[[],[...p]]),K((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([s,p])=>({prev:s.map(([c])=>c),next:p.map(([c])=>c)})),Q({prev:[],next:[]}),Be(2,1),m(([s,p])=>s.prev.length{let i=new g,a=i.pipe(Z(),ie(!0));if(i.subscribe(({prev:s,next:p})=>{for(let[c]of p)c.classList.remove("md-nav__link--passed"),c.classList.remove("md-nav__link--active");for(let[c,[l]]of s.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",c===s.length-1)}),B("toc.follow")){let s=O(t.pipe(_e(1),m(()=>{})),t.pipe(_e(250),m(()=>"smooth")));i.pipe(b(({prev:p})=>p.length>0),He(o.pipe(ve(se))),re(s)).subscribe(([[{prev:p}],c])=>{let[l]=p[p.length-1];if(l.offsetHeight){let f=cr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2,behavior:c})}}})}return B("navigation.tracking")&&t.pipe(W(a),ee("offset"),_e(250),Ce(1),W(n.pipe(Ce(1))),ct({delay:250}),re(i)).subscribe(([,{prev:s}])=>{let p=ye(),c=s[s.length-1];if(c&&c.length){let[l]=c,{hash:f}=new URL(l.href);p.hash!==f&&(p.hash=f,history.replaceState({},"",`${p}`))}else p.hash="",history.replaceState({},"",`${p}`)}),bs(e,{viewport$:t,header$:r}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function vs(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:a}})=>a),Be(2,1),m(([a,s])=>a>s&&s>0),K()),i=r.pipe(m(({active:a})=>a));return z([i,n]).pipe(m(([a,s])=>!(a&&s)),K(),W(o.pipe(Ce(1))),ie(!0),ct({delay:250}),m(a=>({hidden:a})))}function Ei(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({hidden:s}){e.hidden=s,s?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(a),ee("height")).subscribe(({height:s})=>{e.style.top=`${s+16}px`}),h(e,"click").subscribe(s=>{s.preventDefault(),window.scrollTo({top:0})}),vs(e,{viewport$:t,main$:o,target$:n}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))}function wi({document$:e,viewport$:t}){e.pipe(v(()=>P(".md-ellipsis")),ne(r=>tt(r).pipe(W(e.pipe(Ce(1))),b(o=>o),m(()=>r),Te(1))),b(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,B("content.tooltips")?mt(n,{viewport$:t}).pipe(W(e.pipe(Ce(1))),_(()=>n.removeAttribute("title"))):S})).subscribe(),B("content.tooltips")&&e.pipe(v(()=>P(".md-status")),ne(r=>mt(r,{viewport$:t}))).subscribe()}function Ti({document$:e,tablet$:t}){e.pipe(v(()=>P(".md-toggle--indeterminate")),w(r=>{r.indeterminate=!0,r.checked=!1}),ne(r=>h(r,"change").pipe(Dr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),re(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function gs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Si({document$:e}){e.pipe(v(()=>P("[data-md-scrollfix]")),w(t=>t.removeAttribute("data-md-scrollfix")),b(gs),ne(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Oi({viewport$:e,tablet$:t}){z([ze("search"),t]).pipe(m(([r,o])=>r&&!o),v(r=>I(r).pipe(Ge(r?400:100))),re(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ys(){return location.protocol==="file:"?Tt(`${new URL("search/search_index.js",eo.base)}`).pipe(m(()=>__index),G(1)):je(new URL("search/search_index.json",eo.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ot=Go(),Ut=sn(),Lt=ln(Ut),to=an(),Oe=gn(),hr=Pt("(min-width: 960px)"),Mi=Pt("(min-width: 1220px)"),_i=mn(),eo=xe(),Ai=document.forms.namedItem("search")?ys():Ye,ro=new g;Zn({alert$:ro});var oo=new g;B("navigation.instant")&&oi({location$:Ut,viewport$:Oe,progress$:oo}).subscribe(ot);var Li;((Li=eo.version)==null?void 0:Li.provider)==="mike"&&ci({document$:ot});O(Ut,Lt).pipe(Ge(125)).subscribe(()=>{Je("drawer",!1),Je("search",!1)});to.pipe(b(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=fe("link[rel=prev]");typeof t!="undefined"&<(t);break;case"n":case".":let r=fe("link[rel=next]");typeof r!="undefined"&<(r);break;case"Enter":let o=Ie();o instanceof HTMLLabelElement&&o.click()}});wi({viewport$:Oe,document$:ot});Ti({document$:ot,tablet$:hr});Si({document$:ot});Oi({viewport$:Oe,tablet$:hr});var rt=Kn(Se("header"),{viewport$:Oe}),Ft=ot.pipe(m(()=>Se("main")),v(e=>Gn(e,{viewport$:Oe,header$:rt})),G(1)),xs=O(...ae("consent").map(e=>En(e,{target$:Lt})),...ae("dialog").map(e=>qn(e,{alert$:ro})),...ae("header").map(e=>Yn(e,{viewport$:Oe,header$:rt,main$:Ft})),...ae("palette").map(e=>Jn(e)),...ae("progress").map(e=>Xn(e,{progress$:oo})),...ae("search").map(e=>ui(e,{index$:Ai,keyboard$:to})),...ae("source").map(e=>gi(e))),Es=C(()=>O(...ae("announce").map(e=>xn(e)),...ae("content").map(e=>zn(e,{viewport$:Oe,target$:Lt,print$:_i})),...ae("content").map(e=>B("search.highlight")?di(e,{index$:Ai,location$:Ut}):S),...ae("header-title").map(e=>Bn(e,{viewport$:Oe,header$:rt})),...ae("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Nr(Mi,()=>Zr(e,{viewport$:Oe,header$:rt,main$:Ft})):Nr(hr,()=>Zr(e,{viewport$:Oe,header$:rt,main$:Ft}))),...ae("tabs").map(e=>yi(e,{viewport$:Oe,header$:rt})),...ae("toc").map(e=>xi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Lt})),...ae("top").map(e=>Ei(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Lt})))),Ci=ot.pipe(v(()=>Es),Re(xs),G(1));Ci.subscribe();window.document$=ot;window.location$=Ut;window.target$=Lt;window.keyboard$=to;window.viewport$=Oe;window.tablet$=hr;window.screen$=Mi;window.print$=_i;window.alert$=ro;window.progress$=oo;window.component$=Ci;})(); +//# sourceMappingURL=bundle.525ec568.min.js.map + diff --git a/assets/javascripts/bundle.525ec568.min.js.map b/assets/javascripts/bundle.525ec568.min.js.map new file mode 100644 index 00000000..ef5d8d34 --- /dev/null +++ b/assets/javascripts/bundle.525ec568.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n *\n * @class BehaviorSubject\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an + + +
+

This document attempts to describe the architecture and design of the second version of MeshCentral on which work started in late 2016. The document covers the overview of the design, goes in details about the protocol and various decisions and trade-offs. This document is intended for anyone that wants to understand the inner workings of MeshCentral or someone that wants to make a security review of the software. The software and added documentation and tutorial videos are available at: https://www.meshcommander.com/meshcentral2

+

Introduction

+

MeshCentral is a free open source web-based remote computer management software. After over 8 years of working on the first version of MeshCentral, work as moved to version 2 which this document described. In 2010, when MeshCentral v1 was first designed, the Internet was very different. HTML5 and WebSocket did not exists, no such thing as a software container, etc. With MeshCentral version 2, a complete redesign was made to make the software much more in line with modern Internet deployment models.

+

The advent of NodeJS, WebSocket, WebRTC and other web technologies coming out in the last 10 years has really made the design of MeshCentral v2 not only possible, but quite amazing. Being able to use a single programming language across platforms to JavaScript. Being able to easily exchange objects using web socket and JSON, being able to scale with WebRTC, deploy quickly with containers, etc. Looking back at the incredible advances in web technologies lead to an almost mandatory MeshCentral redesign.

+

Goals & Requirements

+

The goal of MeshCentral is to be the best open source remote management software in the world. Remote computer management is a big area with many different usages and requirements. To best suite this, it’s important to have software that is as flexible as possible. Additionally, there are many other goals:

+
    +
  • Must be quick and easy to install.
  • +
  • Must install on all major operating systems and platforms.
  • +
  • Can be deployed on small computers and the cloud.
  • +
  • Can be deployed within containers.
  • +
  • Can be deployed in many network environments.
  • +
  • Must support both software agent and Intel® AMT hardware agent.
  • +
  • Must only use open source dependencies.
  • +
  • Must provide all basic remote management features (desktop, terminal, files…)
  • +
  • Must use the network efficiently.
  • +
  • Must have a real time user interface.
  • +
  • Must be easy to use.
  • +
  • Must be fast.
  • +
  • Etc.
  • +
+

Basically, all the requirements you would expect from open source software that can go viral. Since this software is sponsored by Intel, it’s going to support Intel® AMT really well, making it possible to manage a remote computer regardless of its OS or power state. Intel® AMT is not required to use this software, however it’s a great fit.

+

Design Overview

+

In this section, we do a very high level overview of MeshCentral’s design. MeshCentral has 3 big components: the server, the agent and the web application.

+

+

There is of course more software that support these 3 components like the Windows Server Installer, ClickOnce application, MeshCentral Discovery Tool and more. These will be covered later. Most of the document will focus on these 3 main components. Another component that is significant but not part of the software itself is Intel® AMT (Intel® Active Management Technology). MeshCentral supports Intel AMT that acts like an optional hardware based agent for MeshCentral.

+

When it comes to programming languages used, MeshCentral is mostly built with JavaScript with the agent having significant portable C code. This makes things pretty simple since the browser, server and agents can share some of the code. More importantly, JavaScript is great at parsing JSON and so, the main protocol used between the components is JSON over Web Socket.

+

+

It’s important to note that while JavaScript is used in all 3 components, the JavaScript runtime is very different. The JavaScript written to run within a browser sandbox uses different calls than the one running in NodeJS on the server or on the agent with DukTape.

+

This is probably a good time to introduce DukTape (https://www.duktape.org/). Unlike the browser and NodeJS JavaScript engines, DukTape is a less known JavaScript runtime written in C. The agent is built in C code with little smarts other than being able to securely connect back to the server. The server then pushes to the agent a JavaScript file that the agent runs. This makes the agent very flexible since a developers can quickly change the JavaScript that is pushed to the agent and change the agent’s behavior instantly.

+

Another interesting design decision is that MeshCentral makes almost no use of RESTful API’s. Instead, almost everything is done using WebSocket. This allows JSON objects to be exchanged fully asynchronously. There is no pushing the refresh button or polling as events are sent by all actors in real time.

+

MeshCentral server

+

The MeshCentral server is a NodeJS application that is published on NPM at: https://www.npmjs.com/package/meshcentral Many administrators can get started quickly using “npm install meshcentral” once NodeJS is installed. MeshCentral will work on Node 6.x and higher.

+

Dependencies

+

The server makes use of the following dependencies on NPM. These are all automatically installed by NPM when installing MeshCentral.

+

Can be found in the file: MeshCentralServer.njsproj

+

The main takeaway is that MeshCentral is mostly an ExpressJS application. This is not a complete list of dependencies as many of these packages have their own dependencies creating a large tree. The security of these packages is a concern and all of the dependency tree is a concern. In addition to the dependencies that are “hard coded”, there are a few more that are installed only when needed. These are:

+

node-windows

+

greenlock, le-store-certbot, le-challenge-fs: Installed on all Windows install. Allows background service install:

+

le-acme-core: Installed only when Let’s Encrypt must be used:

+

mongojs: Installed when MongoDB is in used.

+

nodemailer: Installed when SMTP server support is in used.

+

MeshCentral will run npm install automatically when any of these optional modules are needed but not currently available.

+

Understanding the different modes: LAN, WAN and Hybrid

+
+ +
+ +

Code files and folders

+

Someone would think the server is rather simple when taking a look at the MeshCentral server code files. At a high level, the entire server has 3 folders, 3 text files and a manageable number of .js files that are fairly self-descriptive. Here is a list of the source files and folders.

+

Folders

+

agents: Compiled agents, install scripts, tools and agent JavaScript.

+

public: Static web elements such as images, CSS, HTML and more.

+

views: Main web application, login screen and messenger app.

+

Configuration & text files

+

package.json: Description of the MeshCentral package on NPM. +sample-config.json: A sample “config.json” file to get started. +readme.txt: Readme file published with the MeshCentral package.

+

Code files

+
 amtevents.js             | Used to decode Intel AMT WSMAN events.
+ amtmanager.js            | Used to handle Intel AMT/CIRA things.
+ amtprovisioningserver.js | Used to Provision Intel AMT on a Local Network.
+ amtscanner.js            | Used to scan a local network for Intel AMT machines.    
+ amtscript.js             | Used to run Intel AMT scripts from MeshCommander.       
+ certoperations.js        | Used to generate and perform certificate operations.    
+ common.js                | Various commonly used methods.
+ crowdsec.js              | Used to handle all crowdsec security features                     
+ db.js                    | Used to access the MongoDB or NeDB database.            
+ exeHandler.js            | Used to modify windows executables.
+ firebase.js              | Used to handle Google Firebase things.
+ interceptor.js           | Used to insert credentials in an HTTP stream.           
+ letsencrypt.js           | Used to obtain and use a Let’s Encrypt certificate.
+ mcrec.js                 | Standalone Session Recording Indexer.
+ meshaccelerator.js       | Used to offload RSA sign to other CPU cores.            
+ meshagent.js             | Used to communicate to agents.
+ meshbot.js               | Sample bot to connect to meshcentral and preform various tasks                     
+ meshcentral.js           | The is the main module, gets the server started.
+ meshctrl.js              | MeshCtrl performs command line actions on a MeshCentral server.
+ meshcore.js              | Main Agent Code that runs on your remote devices.
+ meshdesktopmultiplex.js  | Used to handle remote desktop multiplexing.
+ meshdevicefile.js        | Used to handle file download requests.
+ meshipkvm.js             | Used to handle IP KVM integration
+ meshmail.js              | Used to send SMTP mails.
+ meshmessaging.js         | Used to handle all users messaging methods like 2FA.
+ meshrelay.js             | Used to relay agent and browser web socket connections. 
+ meshscanner.js           | MeshCentral server discovery when in LAN mode.
+ meshsms.js               | Used to handle all users sms methods.
+ meshuser.js              | Used to communicate with browsers.                      
+ mpsserver.js             | Used to communicate to Intel® AMT CIRA.
+ mqttbroker.js            | Used to create/handle an MQTT broker (beta)               
+ multiserver.js           | Used for server-to-server communication.                
+ pass.js                  | Performs password hash + salt.                          
+ redirserver.js           | Used to handle HTTP traffic.                            
+ swarmserver.js           | Used to upgrade legacy MeshCentralv1 agents. 
+ webauthn.js              | Handles all WebAuthN things.
+ webrelayserver.js        | Used for all HTTP/HTTPS web relaying from agents.          
+ webserver.js             | Handles HTTPS traffic.                                  
+ winservice.js            | Server background install on Windows.
+
+

At a high level, the MeshCentral.js file will get the server started. By default, it will start the webserver.js on port 443, redirectserver.js on port 80 and mpssrver.js on port 4433. The webserver.js file will create a meshuser.js or meshagent.js instance each time a user or agent connects. The other files support various usages, but this is the basic working on the server.

+

Server database

+

One of the big design decision on the server is its database. We want something that scales and so, opted to deal with the good and the bad of a NoSQL database, MongoDB. On the other hand, we want the server to be really simple to install for people who want to try it out or want to manage 100 computers or less. We don’t want the added learning curve of MongoDB for people that don’t really need it. It turned out, we can have both. NeDB is a NPM package that provides a simple MongoDB-like API while being completely implemented in NodeJS. For most people, this is plenty good to get started.

+

By default, MeshCentral will just create and use a NeDB database, but can be configured to use MongoDB. The internal code path for both databases are almost exactly identical so the “db.js” file handles both, almost the same way and the exact database in use is completely abstracted from the rest of the server code.

+

Certificates

+

MeshCentral makes use of many certificates to accomplish many security tasks. When first running the server or an agent, both of these actors will generate certificates. The agent will generate one or two certificates on the first run and the server will generate four certificates.

+

+

In this section we review what certificates are created, what are their uses and how they are stored. Most administrators using MeshCentral will not need a deep understanding of this section to run the server, however, a basic understanding of this section can help understand how to best protect the server’s critical security assets.

+

Server Certificates

+

As indicated above, the MeshCentral server creates four certificates when it first runs. It uses ForgeJS to perform certificate creation and all four certificates below are saved in the “meshcentral-data” folder. The four certificates are:

+

Server root

+

root-cert-public.crt

+

This is a self-signed root certificate that is used only to issue the 3 next certificates. This certificate can be useful when it’s installed as a root of trust in some situations. For example, when Intel AMT connects to the MPS server on port 4433, it will correctly connect only if this root certificate is loaded into Intel AMT as a trusted certificate. Browser can also be setup to trust this root certificate in order to create a trusted connection between a browser and the servers HTTPS port. This certificate is RSA3072 unless the option “--fastcert" is used, in that case a RSA2048 certificate is generated.

+

MPS certificate

+

mpsserver-cert-public.crt

+

This is a TLS certificate signed by the root above used as a TLS server certificate on the MPS port 4433. Intel AMT computers will connect to this port and verify the certificate time, common name and that it’s signed by the root above. This certificate is not typically changed, even when the server is running in production. This certificate is always generated as RSA2048 because older Intel AMT firmware will not accept certificates with larger keys.

+

Web certificate

+

webserver-cert-public.crt

+

This is the default certificate used to secure the HTTPS port 443. It is signed by the root above and is the certificate users will first see then connecting the browser to the server. Often, users will need to ignore the browser security warning. This certificate is RSA3072 unless the option “--fastcert" is used, in that case a RSA2048 certificate is generated. In production environments, this certificate is replaced with a real certificate. There are many ways to change this certificate for a more appropriate certificate in production environments:

+
    +
  • You can replace the “webserver-cert-*” files in the “meshcentral-data” folder.
  • +
  • You can use Let’s Encrypt which will override this certificate automatically.
  • +
  • You can use a reverse-proxy in front of the server with “--tlsoffload".
  • +
+

Agent certificate

+

agentserver-cert-public.crt

+

This certificate is used to authenticate the server to agents. It’s signed by the root above and when installing an agent, the hash of this certificate is given to the agent so that it can connect back to the server securely. This certificate is RSA3072 unless the option “--fastcert" is used, in that case a RSA2048 certificate is generated.

+

The “meshcentral-data” folder contains critical server information including private keys therefore, it’s important that it be well protected. It’s important to backup the “meshcentral-data” folder and keep the backup in a secure place. If, for example the “agent certificate” on the server is lost, there is no hope for agents ever be able to connect back to this server. All agents will need to be re-installed with a new trusted certificate.

+

If someone re-installs a server, placing the “meshcentral-data” folder back with these certificates should allow the server to resume normal operations and accept connections for Intel AMT and agents as before.

+

Agent Certificates

+

The mesh agent generates one or two RSA certificates when it first starts. On smaller IoT devices such as a Raspberry Pi, this can take a little while to do and the CPU will spike to 100% during this time. This is normal and only occurs the first time the agent runs.

+

+

The certificates are generated a little differently depending on the platform. On Windows, the Mesh Agent will use Microsoft cryptographic providers to harder the agent root cert. If available, the agent will use the platform TPM to harden the certificate. On other platforms, only one certificate is generated and used for both agent authentication to the server and WebRTC session authentication.

+

Agent root certificate

+

This certificate is the root trust of the agent. The SHA384 hash of this certificates public key is the agent’s identifier on the server. When a agent connects to the server using web socket, it performs a secondary authentication check using this certificate. The server will compute the agent’s identifier after the agent sent a signed proof to the server. This certificate is also used to sign the secondary certificate below when it’s needed.

+

Secondary certificate

+

This is a certificate signed by the agent root above. It’s currently only used by WebRTC to perform dTLS authentication to a remote browser. This certificate does not need to be signed by a trusted CA for WebRTC purposes since the hash of the certificate will be sent to the browser using a trusted path. If the agent root certificate is not hardened using platform cryptography, the secondary certificate is not created and the agent root cert is used for all purposes.

+

A possible attack would occur if someone were to be able to access the agent root certificate. They could impersonate the agent to the server. Agents don’t have any rights to perform management operations on the server or other agents, but by impersonating a agent, a rogue agent would pretend to be an office computer to which administrator would login with their username & password, especially when the root is not hardened. Some care should be taken to protect the “meshagent.db” file and to not give important information to untrusted agents.

+

TLS Security

+

MeshCentral makes heavy use of Transport Layer Security (TLS) and datagram-TLS (dTLS) to authenticate and encrypt network traffic between the browser, server and agent. Configuring TLS settings correctly is essential to making sure communications are secure and to minimize attacks on open ports.

+

Probably the most important TLS configuration is for the MeshCentral server ports 443 and 4433. These two ports are exposed to the Internet and so, should be setup as securely as possible.

+

MeshCentral HTTPS port 443

+

The HTTPS port on the MeshCentral server will only support TLS 1.2 and above, and makes use of only 6 cypher suites:

+
TLS\_ECDHE\_RSA\_WITH\_AES\_256\_GCM\_SHA384 (0xc030) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA384 (0xc028) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA (0xc014) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_128\_GCM\_SHA256 (0xc02f) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA256 (0xc027) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA (0xc013) 
+
+

Note that these cipher suites are all perfect forward secrecy (PFS) suites and are considered cryptographically secure as of the writing of this document. When the server is deployed on the Internet, https://ssllabs.com gives the server an A rating with no known vulnerabilities and no weak ciphers detected.

+

+

SSL Labs confirms that all major browsers should be able to connect correctly to this server.

+

MeshCentral MPS port 4433

+

The Manageability Presence Server (MPS) port 4433 is used for incoming Intel AMT CIRA connections. By default it uses a TLS certificate that is signed by a self-signed root certificates. This port is not intended to be connected to by typical browsers, only Intel AMT should connect to this port. Note that the TLS certificate generated by MeshCentral for port 4433 is RSA 2048bits, this is because older Intel AMT firmware don’t support RSA 3072. Because the port is not secured using a trusted certificate, SSL Labs will not rate the security of this server.

+

+

This is fully expected. Note that SSL Labs will not test servers that are not on port 443. To perform a test like this MeshCentral must be set temporarily with the MPS port set to 443 and the normal HTTPS port set to a different value.

+

Because older Intel AMT computers that only support TLS 1.0 are common, the server supports TLS v1.0, v1.1 and v1.2 with the following 12 cipher suites:

+
TLS\_ECDHE\_RSA\_WITH\_AES\_256\_GCM\_SHA384 (0xc030) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA384 (0xc028) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA (0xc014) 
+TLS\_RSA\_WITH\_AES\_256\_GCM\_SHA384 (0x9d) 
+TLS\_RSA\_WITH\_AES\_256\_CBC\_SHA256 (0x3d) 
+TLS\_RSA\_WITH\_AES\_256\_CBC\_SHA (0x35) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_128\_GCM\_SHA256 (0xc02f) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA256 (0xc027) 
+TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA (0xc013) 
+TLS\_RSA\_WITH\_AES\_128\_GCM\_SHA256 (0x9c) 
+TLS\_RSA\_WITH\_AES\_128\_CBC\_SHA256 (0x3c) 
+TLS\_RSA\_WITH\_AES\_128\_CBC\_SHA (0x2f)  
+
+

The suites starting with “TLS_RSA_” don’t have perfect forward secrecy (PFS) and so, are considered weak by SSL Labs. However, these are generally the suites that are supported by Intel AMT.

+

Agent to server handshake

+

One interesting aspect of MeshCentral’s design is how the agent connects to the server. We wanted a way for the agent to connect to the server that would be similar to how browsers connect to web servers. This allows for a large number of agents to connect just like if a large number of browsers where connecting. All of the infrastructure that helps web server’s scale would be put to use in the same way for agent connections. For example: TLS offload hardware, load balancers, reverse-proxies, web server scaling, etc. could all be put to use. It also makes the server easier to setup because only one port (HTTPS 443) is needed for both users and agents.

+

One big difference between the agent connecting and a typical browser is how the server is authenticated. Browsers have a set of known trusted root certificates. The server’s web certificate is checked for validity including the name, time trusted CA and more. The agent does not have this. Instead, it just has a hash to a private server certificate.

+

The public facing web certificate of the server can change frequently. For example, Let’s Encrypt certificates are valid 90 days. Agents need to be able to validate a specific server for a long time and don’t really need to trust anything else except one specific server. We also don’t want to tie the agents to a specific domain name as we could change that in the future or want to support servers with dynamic IP addresses and no fixed DNS names.

+

To handle all this, the agent performs a TLS connection to the server and will first see the web certificate of the server. It will then exchange a set of web socket binary messages to the server to perform a secondary authentication with the server.

+

+

The secondary check allows the agent to confirm that this server does own the private key of the private certificate expected by the agent. The agent caches the hash of the “outer” web certificate. When re-connecting, if the agent sees the same outer web certificate, it will skip the secondary check. For obvious security raisons, it’s important that the agent not accept any management messages until the secondary check is completed or skipped.

+

To prevent man-in-the-middle attacks, the secondary check also “pins” the outer web certificate. That is, the server both confirms it’s the right server and indicates to the agent the hash of the outer certificate that it must have seen during the TLS connection. The agent must check this hash to make sure there is no attacker in the middle.

+

The agent connection design allows for reverse-proxies and TLS offload hardware. The agent will first connect a TLS session to the offload hardware. Clear traffic flows between the offload hardware and the server which will perform the secondary check if needed.

+

+

To makes all this work, the MeshCentral server must be able to fetch the hash of the outer web certificate from the reverse proxy. In this case, the server does not need the private key to the web certificate. Note that when the outer web certificate is updated, the server may have to perform many secondary checks at the same time causing a server slowdown during this time. To help with this, MeshCentral will offload the RSA signing operation to many slave processes (as many as the CPU core count on the server) to speed this up. In addition, native NodeJS RSA signing is used (not ForgeJS).

+

The details of the secondary certificate check look like the diagram below. To boost speed, the exchange is fully asynchronous and both sides send the first message as soon as the TLS connection completes.

+

+

Note that these messages are binary (not JSON). The agent must be able to connect to the server independently of the JavaScript that is running in DukTape. So this exchange is handled by native C code in the agent. Binary message 1 is sent immediately after the TLS connection is setup. Both sides will send binary message 2 when message 1 is received and message 3 when message 2 is received.

+

In addition, there are two extra messages of interest that can be sent by the agent right at the start. The agent may send the server message number 4 if the secondary check can be skipped and may send binary message number 5 indicating what server hash it expects to verify. Message number 5 is interesting because a server may have many “identities” at the same time, and so, the server will use message number 5 in order to use the right Agent Server certificate.

+

In order to be as secure as possible, all hashes use SHA384 and certificates are RSA3072 and nonces are generated on both sides using a cryptographic random source. The server and agent signatures are computed like this:

+

+

While the server will often skip its RSA signature operation due to the agents caching the outer web certificate, the server must perform an RSA verify to each agent connection. This can’t be skipped but is needed to authenticate the agent.

+

Once connected, the trust relationship between the server and the agent is one-way. That is, the server has management rights on the agent, but the agent does not have any right on the server. This is important since the agent does not, by default, have any credentials to the server. Any agent can connect to the server and claim to be part of a device group.

+

Browser to agent relay and WebRTC

+

Browsers and agents often need to communicate to each other. Data sessions are used for desktop, terminal, file transfers, etc. and must be setup securely.

+

To setup a session between a browser and the agent, the server will send a URL to both sides to connect to. The URL is generated by the server and includes a unique connection token. It is sent to both the browser and agent using the web socket control channel and a JSON message. Both sides perform a websocket connection to the target URL and the server will “pipe” both sessions together to act as a passive relay. For security, the agent will only accept connections to the URL given by the server if the server has the same outer web certificate as its control connection. Also note that in this mode, the session is not end-to-end encrypted. The server is performing a TLS decrypt and re-encrypt and the traffic cost is high as each byte of data has to be received and sent again.

+

+

The relay server is just websocket server that will wait for connections with session tokens. When two connection with the same connection token arrive, the server makes sure that at least one of the two connections is an authenticated user, it then sends the character “c” on both sides to inform both parties that the relay is starting and then pipes both sessions together. Once the session is started, the browser and agent are free to send messages to each other. Note that when the server sends the relay URL to the agent, it also sends to the agent the user’s permissions flags. This may be used by the agent to limit what the user can do on this session.

+

With this design, the flow control between the browser and agent is simple, each session gets its own end-to-end connection and the server will apply appropriate TCP back pressure on both sides as needed.

+

A unique feature of MeshCentral is its use of WebRTC. WebRTC was introduced in major browsers as a way to allow browsers to directly communicate to each other and perform audio/video streaming. The mesh agent has a WebRTC data-only stack that is custom built for this project in C code. It’s compatible with Chrome and Firefox implementations and once a session is set up, allows data to flow directly from the browser to the agent, bypassing the server.

+

+

The use of WebRTC allows MeshCentral to scale better, to offer a faster user experience and lower hosting costs all at the same time. However, WebRTC is not easy, especially when you must maintain the C code for it and have to keep up with browser implementations, but the benefits are clear.

+

To setup WebRTC, browsers typically use STUN and TURN servers to get traffic thru any network obstacles (routers, proxies, firewalls). This infrastructure can be complex to setup especially if an administrator is not familiar with WebRTC concepts. To make things easy, MeshCentral opted to always start by using a websocket relay thru the server to get things started. While a session is active, the browser and agent will attempt to automatically switch the session traffic to WebRTC when possible. This way, the session always works and gets more efficient when network conditions allow.

+

To perform the switch-over, both browser and agent will exchange WebRTC control messages over the newly established web socket relay session.

+

+

In order to differentiate session traffic from WebRTC control traffic, the browser and agent agree to send WebRTC setup traffic using web socket text fragments. All other session traffic is sent using binary fragments. The agent has a special API allowing a session to be piped for a single fragment type. So we can perform a remote desktop session to the agent while trying to setup WebRTC at the same time.

+

The browser will kick off the WebRTC setup sending the initial WebRTC offer with the agent responding with a WebRTC answer. If the WebRTC session gets setup, both sides need to negotiate a clear transition from the web socket session to the WebRTC session. To do this, both sides send a start switch control fragment (this is a text fragment), the other side will respond with an ACK when the web socket session is flushed out and it’s safe to switch.

+

On the agent side, the new WebRTC session inherits the user access rights of the web socket. Currently, the web socket channel is still maintained open. While it’s not strickly needed, the web socket session terminates more cleanly than WebRTC and so, oddly its closure is used to signal the end of the WebRTC session.

+

Messenger

+

MeshCentral includes its own messaging web application it can be used to chat, transfer files and optionally used for audio and video chat. It’s used to support two different usages: User-to-user and user-to-computer communication. In the first usage, two users that are connected to the same MeshCentral server at the same time can chat. If you are a MeshCentral administrator, you can see the list of currently logged in users and hit the chat button to launch a chat invitation. If accepted, the Messenger is open on both sides and the session starts. Alternatively, while managing a remote computer, an administrator can hit the chat button to cause the remote computer to open a web browser to the chat application.

+

+

The chat app is standalone web application that is served by the MeshCentral server using a connection token and title in the URL. Once loaded in its own web frame, the messenger web application will get the connection token and title from the URL and proceed to connect to the URL using web socket. The same web socket relay that is used for browser-to-agent connections is also used in this case for browser-to-browser connections. The server relay acts the same and pipes both sessions together after sending the character “c” to both sides. At this point, the messenger application will show the remote user as connected and chat and file transfers can start. File transfers are just a set of binary messages sent over the web socket session with lots of JSON control messages.

+

Once the web socket session is setup, the messenger application will then attempt to perform a switch over to WebRTC. Both web application start by selecting a random number (not cryptographic) and the highest number will initiate the WebRTC offer. The other party will answer and both sides will trade interface candidates as they are discovered. If successful, the web socket session are flushed and the traffic is switched over to WebRTC. Because the switchover is done cleanly, it can occur while in the middle of a file transfer without the file being corrupted.

+

+

Finally, the web application will determine if the local computer is attached to a microphone and if it has a camera. If so, these options are offered in the chat window and audio/video chat is available for use. The chat app allows for one way setup of audio & video sessions. This is typically what is needed in support scenarios where the audio/video session is one-way.

+

The messenger web application will setup a separate WebRTC connection for audio/video in each direction but the code is present to augment the WebRTC control channel with audio/video which is a bit more efficient but more testing is needed before defaulting to this mode.

+

Additional Resources

+

In addition to this document, there are a growing set of MeshCentral resources at: https://www.meshcommander.com/meshcentral2. This includes an Installer’s documents, a User’s Guide and plenty of YouTube tutorial videos. For developers, it’s best to start on the MeshCentral GitHub repository at: https://github.com/Ylianst/MeshCentral. If any issues are found, it’s best to create a new issue in GitHub or mail ylianst@gmail.com

+

Conclusion

+

MeshCentral is a free, open source and powerful remote management solution that is cross- platform. In this document, we have covered the goals, overview, design and some details of the software. It’s hoped that this document will encourage developers to take a look at MeshCentral for more usages and review its security in detail. MeshCentral’s use of modern web technologies make it a unique and amazing solution for remote management of computers. As with any good software, MeshCentral will continue to be updated and evolve.

+

License

+

MeshCentral and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0

+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/how-to-contribute/images/translation-msg-output.png b/how-to-contribute/images/translation-msg-output.png new file mode 100644 index 00000000..ab94a484 Binary files /dev/null and b/how-to-contribute/images/translation-msg-output.png differ diff --git a/how-to-contribute/index.html b/how-to-contribute/index.html new file mode 100644 index 00000000..6044c37b --- /dev/null +++ b/how-to-contribute/index.html @@ -0,0 +1,1685 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Contribute to MeshCentral - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + +

Contribute to MeshCentral

+

Contributing to MeshCentral via GitHub Pull Request

+

If you're looking to contribute beyond translations, such as updating documentation or enhancing the software by adding features or fixing bugs, the process involves several key steps:

+
    +
  1. +

    Fork the Repository: Start by forking the MeshCentral repository on GitHub. This creates a copy of the repository under your own GitHub account, allowing you to make changes without affecting the original project.

    +
  2. +
  3. +

    Make Your Changes

    +
      +
    • In your forked repository, create a new branch to keep your changes organized. This helps in managing different contributions separately.
    • +
    • Make the necessary changes in your repository. This could involve updating documentation files or modifying code to add new features or fix bugs.
    • +
    +
  4. +
  5. +

    Review Your Changes: Before submitting your work, carefully review the changes you’ve made. Check the "Files Changed" section on GitHub to ensure that all modifications are intended and correctly implemented.

    +
  6. +
  7. +

    Submit a Pull Request

    +
      +
    • Once your changes are ready and reviewed, submit a pull request (PR) from your branch to the master branch of the main MeshCentral repository.
    • +
    • When creating the pull request, provide a clear and detailed description of what changes have been made and why. This helps maintainers understand the purpose of your contributions.
    • +
    +
  8. +
  9. +

    Wait for Review: After submitting your pull request, wait for a project maintainer to review your contribution. Review time can vary depending on the complexity of the changes and the availability of the maintainers.

    +
  10. +
  11. +

    Respond to Feedback: The maintainer may request further modifications or provide feedback on your pull request. Be prepared to make additional changes based on their suggestions to ensure that your contribution meets the project’s standards and requirements.

    +
  12. +
  13. +

    Final Steps: Once your pull request is approved and merged by a maintainer, your contributions will be incorporated into the MeshCentral project. Congratulations, and thank you for helping improve MeshCentral!

    +
  14. +
+
+

Contribute to MeshCentral's Multilingual Support

+

To make MeshCentral multilingual, your contributions are crucial. Follow these steps to translate the interface into various languages.

+
    +
  1. +

    Remove Local Translations: Delete translate.json from your meshcentral-data folder. This file contains your local copy of translations, which may become outdated as new features and texts are added.

    +
  2. +
  3. +

    Access MeshCentral: Ensure you are logged into MeshCentral.

    +
  4. +
  5. Open Translation Tool: Visit https://YOURMESHCENTRALSERVER.COM/translator.htm to access the translation interface.
  6. +
  7. +

    Choose a Language: Select the language you wish to translate from the list provided.

    +
  8. +
  9. +

    Translate Text: Use the search function or scroll through the list to find text segments you want to translate. Utilize the "show no translations only" checkbox to filter untranslated texts.

    +
  10. +
  11. Enter Translations: For each text segment, enter your translation in the bottom box (not the top one) and click SET (F1).
  12. +
  13. +

    Repeat Translation: Continue translating by repeating steps 5 and 6 for other texts as desired.

    +
  14. +
  15. +

    Save and Apply Translations

    +
      +
    • Click SAVE TO SERVER (F3) to save your translations to meshcentral-data/translate.json locally in your MeshCentral server.
    • +
    • Optionally, click SAVE TO FILE (F4) to download the translate.json file for offline review or sharing.
    • +
    +
  16. +
  17. +

    Deploy Translations: Click TRANSLATE SERVER and allow some time for the process to complete (approximately 5-15 minutes depending on server specifications). This command line output will indicate when the translation is complete.
    +

    +
  18. +
  19. +

    Finalize Changes: It’s crucial to restart MeshCentral to ensure that the translated files are picked up correctly.

    +
  20. +
  21. Share your translations: Once a language translation is complete, take the latest translation.json and share it by emailing it to the maintainer (Ylianst, ylianst@gmail.com) or by submitting it to the MeshCentral GitHub repository via a pull request.
  22. +
+
+

Additional Information:

+
    +
  • If you make any changes to default.handlebars, run the translate server to propagate these modifications to the language-specific handlebar files located in node_modules/meshcentral/views/translations.
  • +
+

By following these steps, you help MeshCentral support any language you choose, making it more accessible worldwide. By sharing your translations with us, you also help make these languages available to other users, improving the community and extending the software's reach.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/images/2022-08-04-18-19-19.png b/images/2022-08-04-18-19-19.png new file mode 100644 index 00000000..f2e6b19f Binary files /dev/null and b/images/2022-08-04-18-19-19.png differ diff --git a/images/back.png b/images/back.png new file mode 100644 index 00000000..8dd12ef3 Binary files /dev/null and b/images/back.png differ diff --git a/images/favicon-303x303.png b/images/favicon-303x303.png new file mode 100644 index 00000000..71f5f781 Binary files /dev/null and b/images/favicon-303x303.png differ diff --git a/images/favicon.ico b/images/favicon.ico new file mode 100644 index 00000000..806ba7a8 Binary files /dev/null and b/images/favicon.ico differ diff --git a/index.html b/index.html new file mode 100644 index 00000000..e8241c51 --- /dev/null +++ b/index.html @@ -0,0 +1,1670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

MeshCentral Documentation

+

About

+

MeshCentral is a full computer management web site. With MeshCentral, you can run your own web server to remotely manage and control computers on a local network or anywhere on the internet. Once you get the server started, create device group and download and install an agent on each computer you want to manage. A minute later, the new computer will show up on the web site and you can take control of it. MeshCentral includes full web-based remote desktop, terminal and file management capability.

+

For more information, visit MeshCentral.com.

+

Social Media

+

YouTube

+

Reddit

+

Twitter

+

BlogSpot

+

Documentation

+

The User's Guide contains information every administrator should know including usage, the server configuration file, databases, TLS offloading, Lets Encrypt, IP Filtering, Email setup, embedding, server port aliasing, reverse proxy setup, multi factor authentication, branding & terms of use, HashiCorp Vault support, and SSO.

+

The Installation Guide has detailed instructions for installing the MeshCentral Server on Windows 8.1, Windows 10, Windows 2012 R2, Amazon Linux 2, Raspberry Pi, Microsoft Azure, Google Cloud, Ubuntu 18, Ubuntu 16 and OpenBSD.

+

The Design and Architecture Guide is a short document that includes information on the design overview, dependencies, source code descriptions of each file, certificates, TLS security, the agent to server handshake, browser to agent relay and WebRTC and the messenger service.

+

Video Tutorials

+

You can watch many tutorial videos on the MeshCentral YouTube Channel. Two videos to get started involve installation and basic usages.

+

Installing MeshCentral on Windows, Linux and macOS. +MeshCentral - Installation

+

Basic Usages including installing the agent and remote desktop, terminal and file access. +MeshCentral - Basics

+

MeshCentral support for two-factor authentication. +MeshCentral - Two Factor Authentication

+

How to setup MeshCentral with the NGINX reverse proxy. +MeshCentral - NGINX Reverse Proxy

+

Installing and using the MeshCentral Android agent. +MeshCentral - Android

+

Using MeshCentral Router to port map TCP connections. +MeshCentral - Basics

+

Feedback

+

If you encounter a problem or have a suggestion to improve the product, you may file an issue report

+

If you are filing a problem report, you should include:

+
    +
  • The version of the software you are using
  • +
  • The Operating System and version
  • +
  • The observed output
  • +
  • The expected output
  • +
  • Any troubleshooting you took to resolve the issue yourself
  • +
  • Any other similar reports~~
  • +
+

If you are having issues with the following other products, you should file a report on their respective issue pages +MeshAgent +MeshRouter +License

+

This software is licensed under Apache 2.0.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/install/images/2022-05-16-23-45-01.png b/install/images/2022-05-16-23-45-01.png new file mode 100644 index 00000000..a89c1faf Binary files /dev/null and b/install/images/2022-05-16-23-45-01.png differ diff --git a/install/images/2022-05-16-23-47-10.png b/install/images/2022-05-16-23-47-10.png new file mode 100644 index 00000000..dee2e04a Binary files /dev/null and b/install/images/2022-05-16-23-47-10.png differ diff --git a/install/images/2022-05-16-23-47-36.png b/install/images/2022-05-16-23-47-36.png new file mode 100644 index 00000000..b2762629 Binary files /dev/null and b/install/images/2022-05-16-23-47-36.png differ diff --git a/install/images/2022-05-16-23-53-08.png b/install/images/2022-05-16-23-53-08.png new file mode 100644 index 00000000..0218cded Binary files /dev/null and b/install/images/2022-05-16-23-53-08.png differ diff --git a/install/images/2022-05-17-00-01-10.png b/install/images/2022-05-17-00-01-10.png new file mode 100644 index 00000000..cfaf80f8 Binary files /dev/null and b/install/images/2022-05-17-00-01-10.png differ diff --git a/install/images/2022-05-17-00-01-52.png b/install/images/2022-05-17-00-01-52.png new file mode 100644 index 00000000..697217bd Binary files /dev/null and b/install/images/2022-05-17-00-01-52.png differ diff --git a/install/images/2022-05-17-00-02-25.png b/install/images/2022-05-17-00-02-25.png new file mode 100644 index 00000000..af704235 Binary files /dev/null and b/install/images/2022-05-17-00-02-25.png differ diff --git a/install/images/2022-05-17-00-03-59.png b/install/images/2022-05-17-00-03-59.png new file mode 100644 index 00000000..3b019380 Binary files /dev/null and b/install/images/2022-05-17-00-03-59.png differ diff --git a/install/images/2022-05-17-00-12-10.png b/install/images/2022-05-17-00-12-10.png new file mode 100644 index 00000000..793be731 Binary files /dev/null and b/install/images/2022-05-17-00-12-10.png differ diff --git a/install/images/2022-05-17-00-16-40.png b/install/images/2022-05-17-00-16-40.png new file mode 100644 index 00000000..f0af0be1 Binary files /dev/null and b/install/images/2022-05-17-00-16-40.png differ diff --git a/install/images/2022-05-17-00-19-19.png b/install/images/2022-05-17-00-19-19.png new file mode 100644 index 00000000..cdd49467 Binary files /dev/null and b/install/images/2022-05-17-00-19-19.png differ diff --git a/install/images/2022-05-17-00-29-07.png b/install/images/2022-05-17-00-29-07.png new file mode 100644 index 00000000..e671a01e Binary files /dev/null and b/install/images/2022-05-17-00-29-07.png differ diff --git a/install/images/2022-05-17-00-34-12.png b/install/images/2022-05-17-00-34-12.png new file mode 100644 index 00000000..6838013c Binary files /dev/null and b/install/images/2022-05-17-00-34-12.png differ diff --git a/install/images/2022-05-17-00-34-24.png b/install/images/2022-05-17-00-34-24.png new file mode 100644 index 00000000..2b20030a Binary files /dev/null and b/install/images/2022-05-17-00-34-24.png differ diff --git a/install/images/2022-05-17-00-34-37.png b/install/images/2022-05-17-00-34-37.png new file mode 100644 index 00000000..94a41940 Binary files /dev/null and b/install/images/2022-05-17-00-34-37.png differ diff --git a/install/images/2022-05-17-00-36-30.png b/install/images/2022-05-17-00-36-30.png new file mode 100644 index 00000000..0b557167 Binary files /dev/null and b/install/images/2022-05-17-00-36-30.png differ diff --git a/install/images/2022-05-17-00-36-52.png b/install/images/2022-05-17-00-36-52.png new file mode 100644 index 00000000..b0250b1e Binary files /dev/null and b/install/images/2022-05-17-00-36-52.png differ diff --git a/install/images/2022-05-17-00-37-05.png b/install/images/2022-05-17-00-37-05.png new file mode 100644 index 00000000..6bded651 Binary files /dev/null and b/install/images/2022-05-17-00-37-05.png differ diff --git a/install/images/2022-05-17-00-37-21.png b/install/images/2022-05-17-00-37-21.png new file mode 100644 index 00000000..d0d5d404 Binary files /dev/null and b/install/images/2022-05-17-00-37-21.png differ diff --git a/install/images/2022-05-17-00-37-35.png b/install/images/2022-05-17-00-37-35.png new file mode 100644 index 00000000..c696f731 Binary files /dev/null and b/install/images/2022-05-17-00-37-35.png differ diff --git a/install/images/2022-05-17-00-37-46.png b/install/images/2022-05-17-00-37-46.png new file mode 100644 index 00000000..c9668227 Binary files /dev/null and b/install/images/2022-05-17-00-37-46.png differ diff --git a/install/images/2022-05-17_000542.png b/install/images/2022-05-17_000542.png new file mode 100644 index 00000000..1ac00c3f Binary files /dev/null and b/install/images/2022-05-17_000542.png differ diff --git a/install/images/2022-05-17_003521.png b/install/images/2022-05-17_003521.png new file mode 100644 index 00000000..0df391e9 Binary files /dev/null and b/install/images/2022-05-17_003521.png differ diff --git a/install/index.html b/install/index.html new file mode 100644 index 00000000..eed3e140 --- /dev/null +++ b/install/index.html @@ -0,0 +1,1614 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Quick Start Guide - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Quick Start Guide

+

Installation

+

Getting started is easy. If you don't have it already, install NodeJS. Then, create an empty folder and do this:

+
npm install meshcentral
+node node_modules/meshcentral
+
+

That's it. MeshCentral will set itself up and start managing computers on your local network. By default it will be setup in LAN mode and agents you install will multicast on the local network to find the server. To setup the server so that agents use a well known DNS name and to start customizing your server, go in the "meshcentral-data" folder and edit the config.json file. The configuration file must be valid JSON, you can use this link to validate the file format.

+

For Windows users, you can download the MeshCentral Installer that will automate installation of NodeJS and provide basic configuration of the server. This option is not recommended for advanced users.

+

Win32 MeshCentral Installer

+

By default, MeshCentral will use NeDB as this is the built-in database. For more advanced users, it's recommended to switch to using MongoDB. MeshCentral can be installed on a very small server. A Raspberry Pi or AWS t3.nano running Amazon Linux 2 instance for 5$ a month will do just fine for managing up to a few hundred devices.

+

You can run the MeshCentral Server with --help to get options for background installation.

+

Configuration

+

Once you get MeshCentral installed, the first user account that is created will be the server administrator. So, don't delay and navigate to the login page and create a new account. You can then start using your server right away. A lot of the fun with MeshCentral is the 100's of configuration options that are available in the config.json file. You can put your own branding on the web pages, setup a SMTP email server, SMS services and much more.

+

You can look here for simple config.json, here for a more advanced configuration and here for all possible configuration options. You can also take a look at the tutorial videos for additional help.

+

Video Walkthru

+
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/install/install2/index.html b/install/install2/index.html new file mode 100644 index 00000000..70fe39f1 --- /dev/null +++ b/install/install2/index.html @@ -0,0 +1,3337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Full Install Guide - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Full Install Guide

+

Abstract

+

This guide is specifically intended to help users install MeshCentral from start to finish. Once installed, you can take a look at the MeshCentral user’s guide for information on how to configure MeshCentral for your specific use. In this document, we will look at installing MeshCentral on AWS Linux, Raspberry Pi and Ubuntu.

+

Docker

+

https://github.com/Ylianst/MeshCentral/pkgs/container/meshcentral

+
docker pull ghcr.io/ylianst/meshcentral:master
+
+
+

Warning

+

Do not use the built in mesh update function. Update docker the docker way.

+
+

Docker Compose

+
version: '3'
+services:
+  meshcentral:
+    restart: unless-stopped # always restart the container unless you stop it
+    image: ghcr.io/ylianst/meshcentral:1.1.27 # 1.1.27 is a version number OR use master for the master branch of bug fixes
+    ports:
+      - 80:80 # HTTP
+      - 443:443 # HTTPS
+      - 4433:4433 # AMT (Optional)
+    volumes:
+      - data:/opt/meshcentral/meshcentral-data # config.json and other important files live here
+      - user_files:/opt/meshcentral/meshcentral-files # where file uploads for users live
+      - backup:/opt/meshcentral/meshcentral-backups # location for the meshcentral backups - this should be mounted to an external storage
+      - web:/opt/meshcentral/meshcentral-web # location for site customization files
+volumes:
+  data:
+    driver: local
+  user_files:
+    driver: local
+  backup:
+    driver: local
+  web:
+    driver: local
+
+

Quick Start

+

For some who want to skip this document entirely, there are quick install scripts that will get a MeshCentral2 instance up and running on Linux in a few minutes. These scripts will pretty much do what this document explains very rapidly. Right now, there are two such scripts available:

+

Amazon Linux 2

+

For Amazon EC2 users, that want to manage 100 devices or less. Launch a t3.nano or t3.micro EC2 instance with Amazon Linux 2 with TCP ports 22 (SSH), 80 (HTTP), 443 (HTTPS) and 4433 (CIRA) open. Then login as ec2-user and enter the following commands:

+
wget https://meshcentral.com/scripts/mc-aws-linux2.sh
+chmod 755 mc-aws-linux2.sh
+./mc-aws-linux2.sh
+
+

This will download the fast install script and once run, will install nodejs, meshcentral, setup systemd and start the server. For a larger instance like a t3.small, t3.medium or larger you can run the following that does the same but also installs MongoDB.

+
wget https://meshcentral.com/scripts/mc-aws-linux2-mongo.sh
+chmod 755 mc-aws-linux2-mongo.sh
+./mc-aws-linux2-mongo.sh
+
+

After these scripts are run, try accessing the server using a browser. MeshCentral will take a minute or two to create certificates after that, the server will be up. The first account to be created will be the site administrator – so don’t delay and create an account right away. Once running, move on to the MeshCentral’s user’s guide to configure your new server.

+

Microsoft Azure

+

For 100 devices or less, launch an instance of Ubuntu 18.04 using a small B1s instance. Set the username to default in all lower case and open ports 22, 80, 443 and 3389 using the basic network profile. Then start the instance and run the following lines.

+
wget https://meshcentral.com/scripts/mc-azure-ubuntu1804.sh
+chmod 755 mc-azure-ubuntu1804.sh
+./mc-azure-ubuntu1804.sh
+
+

In this situation, port 3389 will be used to receive Intel AMT CIRA connections instead of port 4433. After these scripts are run, try accessing the server using a browser. MeshCentral will take a minute or two to create certificates after that, the server will be up. The first account to be created will be the site administrator – so don’t delay and create an account right away. Once running, move on to the MeshCentral’s user’s guide to configure your new server.

+

Server Security - Adding Crowdsec

+

MeshCentral has built-in support for a CrowdSec bouncer. This allows MeshCentral to get threat signals from the community and block or CAPTCHA requests coming from known bad IP addresses.

+

Video Walkthru

+
+ +
+ +

Windows Installation

+

MeshCentral is constructed entirely with NodeJS, an asynchronous event driven JavaScript runtime (https://nodejs.org/). A basic understanding on NodeJS may be preferable but not compulsory. MeshCentral server which heavily relies on NodeJS runtime will be able run on almost any computing platform with contemporary operating systems including Windows*, Linux* and macOS*.

+

There are two ways to get MeshCentral setup.

+
    +
  • For Linux*, macOS*, or advanced users can use CLI based NPM tool.
  • +
  • For Windows users, you can use the MeshCentral installation tool.
  • +
+

Windows Installation Tool

+

The MeshCentral installer tool for Microsoft Windows can be downloaded at https://www.meshcommander.com/meshcentral2 or by clicking this link. This tool will automatically detect and install NodeJS if needed. NodeJS will be downloaded from https://nodejs.org checked and installed. We recommend the installer be run on a modern version of Windows (.e.g. Win8.1, Win10, Win Server 2012* or better)

+

+

During installation, the installation tool will prompt for the following settings:

+
    +
  • Multi-user Server : By enabling this option, the server will be open to any user with a web browser app. Users will be able to create accounts and start managing computers associated in their respective accounts.
  • +
+
+

Note

+

If this option is disabled (unchecked), the server will run as a single-user server, no login screen will be presented and MeshCentral application will be limited to the server host machine only.

+
+
    +
  • Auto-update Server: By enabling this option, the server will check new version releases daily and perform automatic update.
  • +
+
+

Note

+

Update check occurs at 0000 between 0100 hours (local time). During update, the server will not be accessible until update is completed.

+
+
    +
  • +

    Server Modes, LAN, WAN or Hybrid:

    +

    LAN mode: Recommended for small installation within a local network. Server host does not need a fixed IP address or DNS record to operate.

    +

    WAN or Hybrid modes: Server host will require a fixed IP address or DNS record to function correctly. If selected, user will need to enter server’s DNS name or static IP address in the Server Name field. This name or IP address will be used by browsers and agents to connect back to the server, this name MUST be correct or the server will not work. If you do not have a fixed name, select LAN mode to get started.

    +
  • +
+

Acquiring a static IP or DNS record is beyond the scope of this document. Please seek advice or consult your network administrator if unsure. If unsure, leave the settings as default (as-is) and proceed setup in LAN mode to manage computers that reside within the same network.

+

Once installed MeshCentral will run as a background Windows Service and can be accessed using a web browser with the link provided by the installer.

+

The installation tool can be run again to perform server update, re-installation or un-installation. When performing an update check, the tool will look at the currently installed version and compare it to the one present on NPM.

+

+

By default, MeshCentral will use TCP ports 80 (HTTP), 443 (HTTPS) and 4433 (Intel® AMT CIRA). The installer will add Windows Defender Firewall rules to allow incoming connections on these ports. In addition, if the server is in LAN or Hybrid mode, an addition rule on UDP port 16990 is added to allow for server discovery.

+

NPM Installation for Advanced Users

+

For advanced users or administrators, MeshCentral can be installed with NPM, a NodeJS package manager that can be accessed via web browser (https://www.npmjs.com/) or command line tool, npm.

+

+
+

Note

+

As a prerequisite, NodeJS and NPM must be installed on host OS and HTTP/HTTPS proxy settings maybe required if server host resides behind a HTTP proxy server.

+
+
    +
  1. +

    To begin, start a command line terminal (Windows Command Prompt or Linux Terminal) and type the following to verify if nodeJS and npm has been installed correctly as shown below + a. To check on nodeJS installed version, type node –v and hit enter key + b. To check on npm installed version, type npm –v and hit enter key

    +
  2. +
  3. +

    If MeshCentral installation is performed on a server host that resides behind a HTTP proxy, NPM’s proxy settings must be updated with respective proxy settings associated with the network environment. Skip this step if not applicable. +

    .e.g. for http proxy `npm config set proxy http://proxy.com:88`
    +.e.g. for https proxy `npm config set https-proxy http://proxy.com:88`
    +

    +
  4. +
  5. Create a new directory MeshCentral and run the NPM install command as shown below: +
    mkdir meshcentral
    +cd meshcentral
    +npm install meshcentral
    +
    +Warning: Do not use sudo in front of npm install meshcentral.
  6. +
  7. Upon download completion, the server can be started with the commands below: +
    node node_modules/meshcentral [arguments]
    +
    + Warning: Do not run MeshCentral by going into the node_modules/meshcentral folder as this may cause auto-install and self-update features to fail. Instead, go into the directory above node_modules and run node node_modules/meshcentral. + +Note: If MeshCentral is started without any arguments, default settings in LAN-only mode will be in effect and user/administrator will only be able to manage computers that reside within the local network.
  8. +
  9. To manage computers over the internet, the server needs to have static IP settings or a DNS record that resolves back to the right server. The mesh agents will be using the mechanism to call home to MeshCentral server. For WAN or Hybrid mode, run one of the commands below +
    node node_modules/meshcentral --cert servername.domain.com
    +node node_modules/meshcentral --cert hostname.domain.com
    +node node_modules/meshcentral --cert 1.2.3.4
    +
    +Note: On first attempt running on WAN or Hybrid Mode:
      +
    • Certificates will be generated for the first time and this may take a few minutes to complete.
    • +
    +
  10. +
+
+

Note

+

At this point, no user account will be created or available for the user hence 1st user account will be the most privileged user with Administrator rights

+
+
    +
  • User is advised to create an admin account immediately by navigating to https://127.0.0.1 with a web browser.
  • +
+

Note: To run MeshCentral as a service, run it using --install argument. Once running, start a web browser and access MeshCentral application with respective URL.

+

Windows Defender Firewall Settings

+

On Windows, the built-in firewall will need to be configured to allow TCP ports 80, 443 and 4433 and sometimes UDP port 16990. The MeshCentral Windows Installer will add incoming rules for these ports automatically. If using the advanced NPM installation or when changing the default ports, it may be needed to add or edit these firewall rules. In this section we look at how to do this.

+

To get started, we need to go in the control panel, click System and Security then Windows Defender Firewall and Advanced Settings on the left side then click on Inbound rules. This will get us on the right place to add or edit firewall rules.

+

+

If the MeshCentral Windows Installer was used, the MeshCentral Server TCP ports and optionally MeshCentral Server UDP ports rules should already be present.

+

Editing the existing rules

+

To edit an existing rule, simply double click on it. To change the allowed inbound ports, go to the Protocols and Ports tab and change the local ports.

+

+

Add new firewall rules

+

To add a new firewall rule, click on the New Rule… then select Port and ok. TCP or UDP and enter the specific local ports needed and ok. Then click ok twice, enter the rule name and ok again.

+

+

Typically, inbound TCP ports 80, 443 and 4433 are used, but the rule can be added with different ports as needed.

+

Amazon Linux 2

+

In this section, we will look at installing MeshCentral on Amazon AWS with Amazon Linux 2. This is a low cost instance and a free tier is available so you can experiment or run a small instance of MeshCentral and it will work perfectly fine.

+

Getting the AWS instance setup

+

On AWS EC2, you can launch an instance and select Amazon Linux 2. In this case, it’s the first option available.

+

+

When launching a new instance, you are asked to use or create a security group with the allowed inbound TCP and UDP ports. The security group should look like this:

+

+

All security group rules should have a source of 0.0.0.0/0 and ::/0. The last rule for port 8080 is only needed if migrating from a MeshCentral1 server, most people don’t need it and should not be added.

+

If you are not going to be managing Intel AMT computers, you can remove port 4433. One can also remove port 80, however it’s needed to get a Let’s Encrypt certificate and useful to route users from the HTTP to the HTTPS web page.

+

For all the following sections, we assume that we are in the ec2-user home path. You can do:

+
cd ~
+
+

This will change the current path to the home folder.

+

Installing NodeJS

+

To get started, launch an instance and start a SSH session to it. You can use SSH on Linux or Putty on Windows to login to the AWS instance.

+

The first thing to do is get NodeJS installed on the instance. We will be installing a long term support (LTS) version of NodeJS. Additional information on how to do this can be found here. We first install the node version manager then activate it and install the NodeJS LTS. It’s done with 3 commands:

+
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
+. ~/.nvm/nvm.sh
+nvm install --lts
+
+

We can test what version of NodeJS is installed using:

+
node -v
+
+

Installing MongoDB

+

If we are going to run a large instance, it’s best to use MongoDB as the database. If you are using a small instance, you can skip installing MongoDB and MeshCentral will use NeDB instead which is a light weight database that is probably great for managing less than 100 computers.

+

If you want to use MongoDB, we can install MongoDB Community Edition. More information on how to do this can be found here.

+

Using nano create the file /etc/yum.repos.d/mongodb-org-4.0.repo:

+
sudo nano /etc/yum.repos.d/mongodb-org-4.0.repo
+
+

Then, put this in it:

+
[mongodb-org-4.0]
+name=MongoDB Repository
+baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/4.0/x86_64/
+gpgcheck=1
+enabled=1
+gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
+
+

This file will setup the repository that we will be using to bet MongoDB. Once done, you can install the package using yum and get it started like this:

+
sudo yum install -y mongodb-org
+sudo service mongod start
+
+

To verify that MongoDB is running, you can enter the MongoDB shell like this:

+
mongo --host 127.0.0.1:27017
+
+

You can leave the shell using Ctrl-C. The database and log files will be create at these locations:

+
/var/log/mongodb
+/var/lib/mongo
+
+

This is useful to know if you want to make a backup of the database file.

+

Port permissions

+

On Linux, ports below 1024 are reserved for the root user. This is a security feature. In our case MeshCentral will need to listen to ports 80 and 443. To allow this, we need to allow node to listen to ports below 1024 like this:

+
whereis node
+node: /home/ec2-user/.nvm/versions/node/v8.11.3/bin/node
+
+sudo setcap cap_net_bind_service=+ep /home/ec2-user/.nvm/versions/node/v8.11.3/bin/node
+
+

We first locate the node binary, using whereis node, we then use the setcap command to add permissions to node. Note that we take the path given by whereis and place it in the setcap command. The setcap command will set permissions allowing node to use ports 1024 and below. This permission may be lost when updating the Linux kernel, so this command may need to be applied again in some case.

+

Installing MeshCentral

+

It’s almost time to install MeshCentral but first, we need to know the public name of our AWS instance, you can run the following command:

+
curl http://169.254.169.254/latest/meta-data/public-hostname
+
+

It will return the public name of the AWS instance, for example:

+
ec2-1-2-3-4.us-west-2.compute.amazonaws.com
+
+

You can use this name, or if you have another registered DNS name pointing to the server instance, you can also use that now. Note that you must setup any alternative name on your own, MeshCentral will not do this for you. This name must be correct and must resolve to this AWS instance as all mesh agents will use this name to connect back to this server.

+

Now, we can use the node package manager (NPM) to install MeshCentral.

+
npm install meshcentral
+
+
+

Warning

+

Do not use sudo in front of npm install meshcentral.

+
+

After that, we can run MeshCentral for the first time. We want to run in WAN-only mode since we will not be managing any computers on the same local network at this server. We also want to create a server with a certificate name that is the same at the AWS instance name. So, we will use --wanonly and --cert [name] arguments to get the server started. For example:

+

node ./node_modules/meshcentral --wanonly --cert ec2-1-2-3-4.us-west-2.compute.amazonaws.com
+
+At this point, the server will create its certificates and start running. +
MeshCentral HTTP redirection web server running on port 80.
+Generating certificates, may take a few minutes...
+Generating root certificate...
+Generating HTTPS certificate...
+Generating MeshAgent certificate...
+Generating Intel AMT MPS certificate...
+Generating Intel AMT console certificate...
+MeshCentral Intel(R) AMT server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:4433.
+MeshCentral HTTPS web server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:443.
+Server has no users, next new account will be site administrator.
+

+

You can now open a browser to the name of the server, for example:

+
https://ec2-1-2-3-4.us-west-2.compute.amazonaws.com
+
+

You will see the server working as expected. You will get a certificate error since the server is used an untrusted certificate for now. Just ignore the error and see the MeshCentral User’s Guide to fix this.

+

+At this point, the server is usable but, there are two things that may still need to be done. First, if we opted to use MongoDB, we have to configure MeshCentral to use a MongoDB database. By default, NeDB will be used which should only be used for small deployments managing less than 100 computers. We also need to automatically start the server when the AWS instance starts.

+

To continue, stop the MeshCentral server with CTRL-C.

+

Configuring for MongoDB

+

By default, MeshCentral uses NeDB with a database file located in ~/meshcentral-data/meshcentral.db. This is great for small servers, but if we opted to install MongoDB, let’s make use of it. We need to edit the config.json file located in the meshcentral-data folder.

+
nano ~/meshcentral-data/config.json
+
+

Then, make the start of the file look like this:

+
{
+  "settings": {
+    "MongoDb": "mongodb://127.0.0.1:27017/meshcentral",
+    "WANonly": true,
+    "_Port": 443,
+    "_RedirPort": 80,
+    "_AllowLoginToken": true,
+    "_AllowFraming": true,
+    "_WebRTC": false,
+    "_ClickOnce": false,
+    "_UserAllowedIP" : "127.0.0.1,::1,192.168.0.100"
+  },
+
+}
+
+

If you start with the default config.json created by MeshCentral, you will need to remove some _ characters in front of settings, mongodb and wanonly. You can also add a _ to other values.

+

You can then same the same and run MeshCentral again. This time, you don’t need to specify the certificate name or --wanonly. You just need to run it like this:

+
node ./node_modules/meshcentral
+
+

The server should now run correctly and use MongoDB. You can even delete the file ~/meshcentral-data/meshcentral.db as it’s not going to be used anymore. You can check that it runs correctly by browsing to the server’s address again and creating a new account. The first account that is created will be administrator for the server, so don’t delay and create the first account right away.

+

Once you are done, we can stop the server again using CTRL-C and in the next sections, we will look at starting the server in the background.

+

Manually starting the server

+

We can manually start and stop the MeshCentral server in the background in different ways. In this section, we are going to create two commands mcstart and mcstop to take care of this. Type this to create the two commands:

+
echo "node ./node_modules/meshcentral > stdout.txt 2> stderr.txt &" > mcstart
+chmod 755 mcstart
+
+echo "pkill –f node_modules/meshcentral" > mcstop
+chmod 755 mcstop
+
+

You can now run the ./mcstart command to launch the server in the background and stop it using the ./mcstop to stop it. This should work pretty well, but if the AWS instance is ever stopped and started again, the server will not automatically launch.

+

Automatically starting the server

+

Since Amazon Linux 2 supports systemd, we are going to use that to auto-start MeshCentral in the background. First, we need to know our own username and group. If we do ls -l in our home folder we get for example:

+
drwxr-xr-x   2 default default 4096 Jul 20 00:03 Desktop
+drwxr-xr-x   2 default default 4096 Jul 20 00:03 Documents
+drwxr-xr-x   2 default default 4096 Jul 20 00:03 Downloads
+…
+
+

Note the username and group name, in this example it’s default for both. We need this information to create the system service description file. To create this file type:

+
sudo pico /etc/systemd/system/meshcentral.service
+
+

Then enter the following lines:

+
[Unit]
+Description=MeshCentral Server
+
+[Service]
+Type=simple
+LimitNOFILE=1000000
+ExecStart=/usr/bin/node /home/default/node_modules/meshcentral
+WorkingDirectory=/home/default
+Environment=NODE_ENV=production
+User=default
+Group=default
+Restart=always
+# Restart service after 10 seconds if node service crashes
+
+RestartSec=10
+# Set port permissions capability
+AmbientCapabilities=cap_net_bind_service
+
+[Install]
+WantedBy=multi-user.target
+
+

Note that the user and group values have to be set correctly for your specific situation. Also, the ExecStart and WorkingDirectory lines includes the path to the user’s home folder which includes the username in it. Make sure that is set correctly.

+

Once this is done, you can now start, enable, stop and disable using the following commands:

+
sudo systemctl enable meshcentral.service
+sudo systemctl start meshcentral.service
+sudo systemctl stop meshcentral.service
+sudo systemctl disable meshcentral.service
+
+

Type in the first two commands to start and enable the service. Enabling the service will make it automatically start when the computer restarts.

+

Once the server is launched, you can access it using a web browser as before. From this point on, refer to the MeshCentral User’s Guide for information on how to configure and use MeshCentral.

+

Raspberry Pi

+

In this section, we will look at installing MeshCentral on the famous Raspberry Pi. This computer’s low price makes it a perfect always-on system for managing computers on a home or small business network. This installation will work on any version of the Raspberry Pi, but version 3 certainly much faster.

+

+

For this installation, we are going to use the Raspbian operating system. You can use the NOOBS version to install this operating system on your Raspberry Pi and install Raspbian. For best performance you can use the Raspbian Stretch Lite image which is much smaller and does not have the X desktop interface. To keep things even smaller, we are not going to be installing MongoDB, instead we are just going to be using NeBD as a database that comes by default with MeshCentral.

+

Installing NodeJS

+

Start by opening a terminal. For all of the installation, we will assume we are the default pi user and we are in the home (~) folder. Let’s get started by installing NodeJS.

+
sudo apt-get update
+sudo apt-get dist-upgrade
+curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash
+sudo apt-get -y install nodejs
+
+

We can now check what version of Node was installed by typing:

+
node -v
+
+

If all goes well, we can now move on to port permissions and installing MeshCentral itself.

+

Port permissions

+

On Linux, ports below 1024 are reserved for the root user. This is a security feature. In our case MeshCentral will need to listen to ports 80 and 443. To allow this, we need to allow node to listen to ports below 1024 like this:

+
whereis node
+node: /usr/bin/node /usr/include/node /usr/share/man/man1/node.1.gz
+
+sudo setcap cap_net_bind_service=+ep /usr/bin/node
+
+

We first locate the node binary, using whereis node, we then use the setcap command to add permissions to node. Note that we take the path given by whereis and place it in the setcap command. The setcap command will set permissions allowing node to use ports 1024 and below. This permission may be lost when updating the Linux kernel, so this command may need to be applied again in some case.

+

Installing MeshCentral

+

Now, we can use the Node Package Manager (NPM) to install MeshCentral.

+
npm install meshcentral
+
+
+

Warning

+

Do not use sudo in front of npm install meshcentral.

+
+

After that, we can run MeshCentral for the first time. We want to run in WAN-only mode since we will not be managing any computers on the same local network at this server. We also want to create a server with a certificate name that is the same at the AWS instance name. So, we will use --wanonly and --cert [name] arguments to get the server started. For example:

+
node node_modules/meshcentral --lanonly --fastcert
+
+

At this point, the server will create its certificates and start running.

+
MeshCentral HTTP redirection web server running on port 80.
+Generating certificates, may take a few minutes...
+Generating root certificate...
+Generating HTTPS certificate...
+Generating MeshAgent certificate...
+Generating Intel AMT MPS certificate...
+Generating Intel AMT console certificate...
+Server name not configured, running in LAN-only mode.
+MeshCentral HTTPS web server running on port 443.
+Server has no users, next new account will be site administrator.
+
+

The next step is to get the IP address of the Raspberry Pi. Use ipconfig:

+
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
+        inet 192.168.2.162  netmask 255.255.255.0  broadcast 192.168.2.255
+        inet6 fe80::8841:34b7:685:14a7  prefixlen 64  scopeid 0x20<link>
+        ether b8:27:eb:01:13:3f  txqueuelen 1000  (Ethernet)
+        RX packets 58325  bytes 72302196 (68.9 MiB)
+        RX errors 0  dropped 271  overruns 0  frame 0
+        TX packets 28457  bytes 3576126 (3.4 MiB)
+        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
+
+

You can now open a browser to the name of the server, for example:

+
https://192.168.2.162
+
+

You will see the server working as expected. You will get a certificate error since the server is used an untrusted certificate for now. Just ignore the error and see the MeshCentral User’s Guide to fix this.

+

+

Configuring for LAN-only mode

+

By default, MeshCentral will assume that you are managing devices both on a local network and on the internet. In the case of this Raspberry Pi installation, we only want to manage device on the local network and so, we can configure MeshCentral to do this. It will adapt the server for this usages. To do this, edit the config.json file:

+
pico ~/meshcentral-data/config.json
+
+

Then, make the start of the file look like this:

+
{
+  "settings": {
+    "LANonly": true,
+    "FastCert": true,
+    "_Port": 443,
+    "_RedirPort": 80,
+    "_AllowLoginToken": true,
+    "_AllowFraming": true,
+    "_WebRTC": false,
+    "_ClickOnce": false,
+    "_UserAllowedIP" : "127.0.0.1,::1,192.168.0.100"
+  },
+
+}
+
+

While we are at it, we can put FastCert to true so that RSA2048 certificates are created instead of RSA3072. This is less secure but runs much faster on small processors like the Raspberry Pi. This is the same as specifying `--fastcert" in the prior section.

+

Manually starting the server

+

We can manually start and stop the MeshCentral server in the background in different ways. In this section, we are going to create two commands mcstart and mcstop to take care of this. Type this to create the two commands:

+
echo "node ./node_modules/meshcentral > stdout.txt 2> stderr.txt &" > mcstart
+chmod 755 mcstart
+
+echo "pkill -f node_modules/meshcentral" > mcstop
+chmod 755 mcstop
+
+

You can now run the ./mcstart command to launch the server in the background and stop it using the ./mcstop to stop it. This should work pretty well, but if the AWS instance is ever stopped and started again, the server will not automatically launch.

+

Automatically starting the server

+

Since Raspbian OS supports systemd, we are going to use that to auto-start MeshCentral in the background. First, we need to know our own username and group. If we do ls -l in our home folder we

+
drwxr-xr-x   2 pi pi  4096 Jul 19 21:23 Desktop
+drwxr-xr-x   2 pi pi  4096 Jun 26 18:23 Documents
+drwxr-xr-x   2 pi pi  4096 Jun 26 18:23 Downloads
+…
+
+

Note the username and group name, in this example it’s pi for both. We need this information to create the system service description file. To create this file type:

+

sudo nano /etc/systemd/system/meshcentral.service
+
+Then enter the following lines:

+
[Unit]
+Description=MeshCentral Server
+
+[Service]
+Type=simple
+LimitNOFILE=1000000
+ExecStart=/usr/bin/node /home/pi/node_modules/meshcentral
+WorkingDirectory=/home/pi
+Environment=NODE_ENV=production
+User=pi
+Group=pi
+Restart=always
+# Restart service after 10 seconds if node service crashes
+RestartSec=10
+# Set port permissions capability
+AmbientCapabilities=cap_net_bind_service
+
+[Install]
+WantedBy=multi-user.target
+
+

Note that the user and group values have to be set correctly for your specific situation. Also, the ExecStart and WorkingDirectory lines includes the path to the user’s home folder which includes the username in it. Make sure that is set correctly.

+

Once this is done, you can now enable, start, stop and disable using the following commands:

+
sudo systemctl enable meshcentral.service
+sudo systemctl start meshcentral.service
+sudo systemctl stop meshcentral.service
+sudo systemctl disable meshcentral.service
+
+

Type in the first two commands to start and enable the service. Enabling the service will make it automatically start when the computer restarts.

+

Once the server is launched, you can access it using a web browser as before. From this point on, refer to the MeshCentral User’s Guide for information on how to configure and use MeshCentral.

+

Ubuntu 18.04

+

In this section, we will look at installing MeshCentral on Ubuntu 18.04 LTS. This is a long term support of Ubuntu freely available for download at https://www.ubuntu.com. Both the desktop and server versions of Ubuntu will work. If this is a remote server and the desktop will not be needed, the server version of Ubuntu can be used. This section will describe a way to install MeshCentral in a user’s home folder, however there is a more secure way to do it, see Increased Security Installation at the end of this section.

+

In all cases, MeshCentral must not be installed as root user. It’s not secure and the instructions below will not work correctly.

+

Installing NodeJS

+

The first thing to do is get NodeJS installed on the computer. We first install the node version manager then activate it and install the NodeJS LTS. It’s done with 4 commands:

+
sudo add-apt-repository universe
+sudo apt update
+sudo apt install nodejs -y
+sudo apt install npm -y
+
+

We can test what version of Node and NPM are installed using:

+
node –v
+npm -v
+
+

Installing MongoDB

+

If we are going to run a large instance, it’s best to use MongoDB as the database. If you are using a small instance, you can skip installing MongoDB and MeshCentral will use NeDB instead which is a light weight database that is probably great for managing less than 100 computers.

+

If you want to use MongoDB, we can install MongoDB Community Edition. More information on how to do this for Ubuntu can be found here.

+

You can install the package using apt and get it started like this:

+
sudo apt install mongodb -y
+
+

Then start the Mongodb service in the background and enable it for auto-restart.

+
sudo systemctl start mongodb
+sudo systemctl enable mongodb
+
+

To verify that MongoDB is running, you can enter the MongoDB shell like this:

+
mongo --host 127.0.0.1:27017
+
+

You can leave the shell using Ctrl-C. The database and log files will be create at these locations:

+
/var/log/mongodb
+/var/lib/mongo
+
+

This is useful to know if you want to make a backup of the database file.

+

Port permissions

+

On Linux, ports below 1024 are reserved for the root user. This is a security feature. In our case MeshCentral will need to listen to ports 80 and 443. To allow this, we need to allow node to listen to ports below 1024 like this:

+
whereis node
+node: /usr/bin/node /usr/include/node /usr/share/man/man1/node.1.gz
+
+sudo setcap cap_net_bind_service=+ep /usr/bin/node
+
+

We first locate the node binary, using whereis node, we then use the setcap command to add permissions to node. Note that we take the path given by whereis and place it in the setcap command. The setcap command will set permissions allowing node to use ports 1024 and below. This permission may be lost when updating the Linux kernel, so this command may need to be applied again in some case.

+

Installing MeshCentral

+

Now, we can use the node package manager (NPM) to install MeshCentral.

+
npm install meshcentral
+
+
+

Warning

+

Do not use sudo in front of npm install meshcentral.

+
+

After that, we can run MeshCentral for the first time. For example:

+
node ./node_modules/meshcentral
+
+

If the computer has a well-known DNS name that users and agents will use to connect to this server, run MeshCentral like this:

+
node ./node_modules/meshcentral --cert example.servername.com
+
+

At this point, the server will create its certificates and start running.

+
MeshCentral HTTP redirection web server running on port 80.
+Generating certificates, may take a few minutes...
+Generating root certificate...
+Generating HTTPS certificate...
+Generating MeshAgent certificate...
+Generating Intel AMT MPS certificate...
+Generating Intel AMT console certificate...
+MeshCentral Intel(R) AMT server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:4433.
+MeshCentral HTTPS web server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:443.
+Server has no users, next new account will be site administrator.
+
+

You can now open a browser and try the server. If you can on the same computer, you navigate to this URL:

+
http://localhost
+
+

If installing on a server that does not have a desktop GUI, use a different computer and enter http:// followed by the IP address or name of the server you installed.

+

You should see the server working as expected. You will get a certificate error since the server is used an untrusted certificate for now. Just ignore the error and see the MeshCentral User’s Guide to fix this.

+

+

At this point, the server is usable but, there are two things that may still need to be done. First, if we opted to use MongoDB, we have to configure MeshCentral to use a MongoDB database. By default, NeDB will be used which should only be used for small deployments managing less than 100 computers. We also need to automatically start the server when the computer starts.

+

To continue, stop the MeshCentral server with CTRL-C.

+

Configuring for MongoDB

+

By default, MeshCentral uses NeDB with a database file located in ~/meshcentral-data/meshcentral.db. This is great for small servers, but if we opted to install MongoDB, let’s make use of it. We need to edit the config.json file located in the meshcentral-data folder.

+
pico ~/meshcentral-data/config.json
+
+

Then, make the start of the file look like this:

+
{
+  "settings": {
+    "MongoDb": "mongodb://127.0.0.1:27017/meshcentral",
+    "WANonly": true,
+    "_Port": 443,
+    "_RedirPort": 80,
+    "_AllowLoginToken": true,
+    "_AllowFraming": true,
+    "_WebRTC": false,
+    "_ClickOnce": false,
+    "_UserAllowedIP" : "127.0.0.1,::1,192.168.0.100"
+  },
+
+}
+
+

If you start with the default config.json created by MeshCentral, you will need to remove some _ characters in front of settings, mongodb and wanonly. You can also add a _ to other values. For details on all of the config.json options, including the WANonly option, refer to the MeshCentral User’s Guide.

+

You can then save the config.json file and run MeshCentral again. This time, you don’t need to specify the certificate name. You just need to run it like this:

+
node ./node_modules/meshcentral
+
+

The server should now run correctly and use MongoDB. You can even delete the file ~/meshcentral-data/meshcentral.db as it’s not going to be used anymore. You can check that it runs correctly by browsing to the server’s address again and creating a new account. The first account that is created will be administrator for the server, so don’t delay and create the first account right away.

+

Once you are done, we can stop the server again using CTRL-C and in the next sections, we will look at starting the server in the background.

+

Manually starting the server

+

We can manually start and stop the MeshCentral server in the background in different ways. In this section, we are going to create two commands mcstart and mcstop to take care of this. Type this to create the two commands:

+
echo "node ./node_modules/meshcentral > stdout.txt 2> stderr.txt &" > mcstart
+chmod 755 mcstart
+
+echo "pkill –f node_modules/meshcentral" > mcstop
+chmod 755 mcstop
+
+

You can now run the ./mcstart command to launch the server in the background and stop it using the ./mcstop to stop it. This should work pretty well, but if the AWS instance is ever stopped and started again, the server will not automatically launch.

+

Automatically starting the server

+

Since Ubuntu 18.04 supports systemd, we are going to use that to auto-start MeshCentral in the background. First, we need to know our own username and group. If we do ls -l in our home folder we get for example:

+
drwxr-xr-x   2 default default 4096 Jul 20 00:03 Desktop
+drwxr-xr-x   2 default default 4096 Jul 20 00:03 Documents
+drwxr-xr-x   2 default default 4096 Jul 20 00:03 Downloads
+…
+
+

Note the username and group name, in this example it’s default for both. We need this information to create the system service description file. To create this file type:

+
sudo pico /etc/systemd/system/meshcentral.service
+
+

Then enter the following lines:

+
[Unit]
+Description=MeshCentral Server
+
+[Service]
+Type=simple
+LimitNOFILE=1000000
+ExecStart=/usr/bin/node /home/default/node_modules/meshcentral
+WorkingDirectory=/home/default
+Environment=NODE_ENV=production
+User=default
+Group=default
+Restart=always
+# Restart service after 10 seconds if node service crashes
+RestartSec=10
+# Set port permissions capability
+AmbientCapabilities=cap_net_bind_service
+
+[Install]
+WantedBy=multi-user.target
+
+

Note that the user and group values have to be set correctly for your specific situation. Also, the ExecStart and WorkingDirectory lines includes the path to the user’s home folder which includes the username in it. Make sure that is set correctly. Lastly the path to node may need to be changed. Type whereis node to find the correct path.

+

Once this is done, you can now start, enable, stop and disable using the following commands:

+
sudo systemctl enable meshcentral.service
+sudo systemctl start meshcentral.service
+sudo systemctl stop meshcentral.service
+sudo systemctl disable meshcentral.service
+
+

Type in the first two commands to start and enable the service. Enabling the service will make it automatically start when the computer restarts.

+

Once the server is launched, you can access it using a web browser as before. From this point on, refer to the MeshCentral User’s Guide for information on how to configure and use MeshCentral.

+

Increased Security Installation

+

On Debian based Linux distributions like Ubuntu, a better and more secure way to install MeshCentral is to have it run within a user account this restricted privileges. When installed like this, the self-update capability of MeshCentral will not work. Instead of installing MeshCentral in the user’s home folder, we install it in /opt/meshcentral and we create a meshcentral user that does not have rights to login or change any of the MeshCentral files. To do this, start by creating a new user called meshcentral

+
sudo useradd -r -d /opt/meshcentral -s /sbin/nologin meshcentral
+
+

We can then create the installation folder, install and change permissions of the files so that the meshcentral account gets read-only access to the files.

+
sudo mkdir /opt/meshcentral
+cd /opt/meshcentral
+sudo npm install meshcentral
+sudo -u meshcentral node ./node_modules/meshcentral
+
+

The last line will run MeshCentral manually and allow it to install any missing modules and create the MeshCentral data folders. Once it’s running, press CTRL-C and continue. The following two lines will change the ownership of files to the meshcentral user and restrict access to the files.

+
sudo chown -R meshcentral:meshcentral /opt/meshcentral
+sudo chmod -R 755 /opt/meshcentral/meshcentral-*
+
+

To make this work, you will need to make MeshCentral work with MongoDB because the /meshcentral-data folder will be read-only. In addition, MeshCentral will not be able to update itself since the account does not have write access to the /node_modules files, so the update will have to be manual. First used systemctl to stop the MeshCentral server process, than use this:

+
cd /opt/meshcentral
+sudo npm install meshcentral
+sudo -u meshcentral node ./node_modules/meshcentral
+sudo chown -R meshcentral:meshcentral /opt/meshcentral
+
+

This will perform the update to the latest server on NPM and re-set the permissions so that the meshcentral user account has read-only access again. You can then use systemctl to make the server run again.

+

MeshCentral allows users to upload and download files stores in the server’s meshcentral-files folder. In an increased security setup, we still want the server to be able to read and write files to this folder and we can allow this with:

+
sudo chmod -R 755 /opt/meshcentral/meshcentral-files
+
+

If you plan on using the increased security installation along with MeshCentral built-in Let’s Encrypt support you will need to type the following commands to make the letsencrypt folder in meshcentral-data writable.

+
sudo mkdir /opt/meshcentral/meshcentral-data
+sudo mkdir /opt/meshcentral/meshcentral-data/letsencrypt
+sudo chmod -R 755 /opt/meshcentral/meshcentral-data/letsencrypt
+
+

This will allow the server to get and periodically update its Let’s Encrypt certificate. If this is not done, the server will generate an ACCES: permission denied exception.

+

Restore backup in Ubuntu

+
    +
  • Stop Meshcentral service sudo systemctl stop meshcentral.service
  • +
  • In your old server, get your backup : meshcentral-data folder, and mongodump-xxxx.archive
  • +
  • In the new server, replace the actual meshcentral-data with your backup (it will handle your LestEncrypt cert also)
  • +
  • Restore mongodb : mongorestore --archive=mongodump-xxxx.archive
  • +
  • Restart meshcentral.service sudo systemctl start meshcentral.service
  • +
+

Microsoft Azure

+

In this section, we will look installing MeshCentral on Microsoft Azure. Microsoft Azure offers many operating system options and we will be selecting Ubuntu Server as our choice. From the Azure portal, we select Virtual machines on the left and Add.

+

+

Once you click on Ubuntu Server, you will see a list of available versions. In this example, we selected Ubuntu 18.04 LTS (Long Term Support). We then have to create an instance name and a way to authenticate to the instance.

+

+

Next is the type of instance to launch. Any instance will do including the B1s which is the smallest possible instance. Of course, as you manage more computers, using an instance that is a bit more powerful is a good idea.

+

+

After selecting the instance type, you can configure storage. 30 gigabytes is plenty. Then the Network Security Group. This is where it’s important to open at least TCP ports 22, 80 and 443.

+

+

Optionally if you wish to use the instance with Intel AMT, open port 4433. In addition port 8080 must be open if you are migrating from MeshCentral1 (not typical).

+

Lastly we launch the instance, it will take a few minutes to setup.

+

+

You can then find the public IP address and use a SSH client like PUTTY on Windows to connect to the instance and start getting MeshCentral setup. From this point on, just use the Ubuntu section above to complete the installation.

+

Google Cloud

+

In this section, we will look installing MeshCentral on Google Cloud. You can sign up easily at https://cloud.google.com/ and you can run a small instance for less than 5$ a month.

+

+

Once you have create an account, you can go to the main console and on the left side, go to Compute Engine and create a new VM instance. For our demonstration, we are going to create the smallest instance possible which is a single shared CPU and only 0.6 gigs of RAM.

+

+

We select the proper settings and select Ubuntu 18.04 LTS Minimal as the boot operating system. This is convenient as we already covered how to install MeshCentral on this operating system.

+

+

Make sure to allow HTTP and HTTPS traffic. Setup like this, we will not be able to manage Intel AMT unless we also open TCP port 4433. Once done with all these options, we can launch the VM instance.

+

+

The new instance will take a few minutes to start up. An interesting feature of Google Cloud is that you can access the VM instance shell directly from the web browser. No need for a separate SSH client. This is exactly what we need and we opt to go ahead and option the web console.

+

+

If will log you in automatically, no additional credentials needed. We can then follow the Ubuntu 18.04 LTS section above to complete the installation. If you opt for a very small instance, it’s probably a good idea to skip installing MongoDB. Just to get started quickly, we can use the following commands:

+
sudo apt update
+sudo apt install nodejs -y
+sudo apt install npm -y
+sudo setcap cap_net_bind_service=+ep /usr/bin/node
+npm install meshcentral
+node ./node_modules/meshcentral --fastcert –wanonly --cert 35.227.45.84
+
+
+

Warning

+

Do not use sudo in front of npm install meshcentral.

+
+

This will install node and npm. Will allow non-root access to ports 80 and 443 and install and start MeshCentral. Because this example uses a very small server instance, we opted to use the fastcert option to create RSA 2048 certificates (the default is RSA 3072 which is more secure).

+

We use the wantonly option because MeshCentral will not be managing computers on a local network, and for this demonstration just used the external IP address of the instance as the server name.

+

If you plan on using an instance without the Intel AMT CIRA port being open (TCP 4433), it’s recommended to add --mpsport 0 so to inform MeshCentral that this port is not open and to not offer Intel AMT CIRA features.

+

Of course, this set of commands is just to get the server started quickly. Follow the Ubuntu 18.04 instructions to setup the server to automatically start using system.

+

Ubuntu 16.04

+

In this section, we will look at installing MeshCentral on Ubuntu 16.04 LTS. This is the same installation at Ubuntu 18.04 LTS, however you need to install NodeJS in a special way. If you use apt install node, you will get an older version 4.x of NodeJS that will not work with MeshCentral.

+

Installing NodeJS

+

The first thing to do is get NodeJS installed on the computer. We first install the node version manager then activate it and install the NodeJS LTS. It’s done with 3 commands:

+
cd ~
+wget https://deb.nodesource.com/setup_8.x
+sudo bash setup_8.x
+sudo apt-get –y install nodejs
+
+

We can test what version of Node and NPM are installed using:

+
node –v
+npm -v
+
+

You should see Node version 8 and NPM version 5. At this point, you can continue installing MeshCentral using the Ubuntu 18.04 installation instructions.

+

OpenBSD 6.4

+

In this section, we will look at installing MeshCentral on OpenBSD 6.4. This section was originally written by Daulton and placed here with this permission. The original instructions are located at: https://daulton.ca/meshcentral-server-on-openbsd/. The section will setup MeshCentral on non-standard ports HTTPS/3000 and HTTP/3001. Thank you to Daulton for his contribution.

+

Installing MongoDB

+

Install the Mongodb package.

+
pkg_add mongodb
+
+

Start and enable Mongodb at boot.

+
rcctl start mongod
+rcctl enable mongod
+
+

Temporary remount /usr with wxallowed while we compile the port. For Cloud VPS they usually only have a root partition instead of how OpenBSD splits it up by default, you will need to edit /etc/fstab and add wxallowed to the options for the root partition and then reboot. Assure to remove this from the fstab options after you are done.

+
mount -r -o wxallowed /usr/
+
+

Installing NodeJS

+

Install NodeJS from ports as it is not available by a package.

+
$ cd /tmp
+$ ftp https://cdn.openbsd.org/pub/OpenBSD/$(uname -r)/{ports.tar.gz,SHA256.sig}
+# cd /usr
+# tar xzf /tmp/ports.tar.gz
+# cd /usr/ports/lang/node
+# make install
+# make clean
+
+

Installing MeshCentral

+

Create the MeshCentral user. The parameters used here are important as we will not let this user login, it has no home directory, and its class is set to daemon. In line with the OpenBSD daemon user naming scheme, we preface the username with an underscore _ to make it easily identifiable as a daemon user.

+
useradd -s /sbin/nologin -d /nonexistent -L daemon -u 446 _meshcentral
+
+

Let’s install MeshCentral and adjust the permissions.

+
mkdir -p /usr/local/meshcentral
+cd /usr/local/meshcentral
+npm install meshcentral
+chown -R _meshcentral:_meshcentral /usr/local/meshcentral
+
+

Configuring for MongoDB and adjusting some other settings such as the network port. Open up the following config in an editor then, make the start of the file look like below. If the setting does not exist yet, just add it below one of the ones we are adjusting in the main settings block.

+

If you start with the default config.json created by MeshCentral, you will need to remove some underscore character in front of settings to enable the setting, such as mongodb and wanonly. You can also add an underscore to other values. For details on all of the config.json options, including the WANonly option, refer to the MeshCentral User’s Guide.

+

Before you can edit the configuration, start the Meshcentral briefly so it generates the default configurations and certificates. Once you see that it says "MeshCentral HTTPS server running...", Ctrl-C to exit then edit the configuration file next.

+
cd /usr/local/meshcentral/node_modules/meshcentral/ && doas -u _meshcentral /usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral/meshcentral.js --launch
+
+

Edit the MeshCentral config.json. For example using vi:

+
vi /usr/local/meshcentral/meshcentral-data/config.json
+
+

In the settings section, set the following key value pairs:

+
{
+"settings": {
+"Cert": "meshcentral.example.com",
+"MongoDb": "mongodb://127.0.0.1:27017/meshcentral",
+"WANonly": true,
+"Port": 3000,
+"ExactPorts": true,
+"RedirPort": 3001,
+"allowLoginToken": true,
+"allowFraming": true,
+"NewAccounts": 0,
+},
+
+}
+
+

Add the following to the root crontab to start MeshCentral at boot. Edit the root crontab by doing the following command as root: crontab -e

+
@reboot cd /usr/local/meshcentral/node_modules/meshcentral/ && doas -u _meshcentral /usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral/meshcentral.js --launch
+
+

As root launch Meshcentral while it installs mongojs, once that finishes and Meshcentral launches close it by doing Ctrl-C. Adjust the permissions again as we ran Meshcentral and it generated new files we need to change the ownership of.

+

/usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral +

chown -R _meshcentral:_meshcentral /usr/local/meshcentral
+

+
+

Warning

+

Do not keep this running or use this command in the future to start the Meshcentral server as it starts the server as root!

+
+

This is a reference /etc/pf.conf for you to keep your server secure. Add any locally connected networks which should have access and any public IP address of a network which will have client PCs connect from to target_whitelist table. Add your own home and/or business IP to my_own_IPs table.

+
ext_if = vio0
+set reassemble yes
+set block-policy return
+set loginterface egress
+set ruleset-optimization basic
+set skip on lo
+
+icmp_types = "{ 0, 8, 3, 4, 11, 30 }"
+
+table <target_whitelist> const { 45.63.15.84, 10.18.5.0/24 }
+table <my_own_IPs> const { 45.63.15.84 }
+table <bruteforce>
+
+match in all scrub (no-df max-mss 1440)
+match out all scrub (no-df max-mss 1440)
+
+block in quick log from urpf-failed label uRPF
+block quick log from <fail2ban>
+
+block in from no-route to any
+block in from urpf-failed to any
+block in quick on $ext_if from any to 255.255.255.255
+block in log quick on $ext_if from { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 255.255.255.255/32 } to any antispoof for $ext_if
+block log all
+
+pass in quick inet proto icmp icmp-type $icmp_types
+pass in quick inet6 proto icmp6
+
+pass in quick proto tcp from <my_own_IPs> \
+to (egress) port { 22 } \
+flags S/SA modulate state \
+(max-src-conn 5, max-src-conn-rate 5/5, overload <bruteforce> flush global)
+
+pass in quick inet proto tcp from <target_whitelist> to port 3000
+pass in quick inet6 proto tcp from <target_whitelist> to port 3000
+
+block in quick log on egress all
+
+pass out quick on egress proto tcp from any to any modulate state
+pass out quick on egress proto udp from any to any keep state
+pass out quick on egress proto icmp from any to any keep state
+pass out quick on egress proto icmp6 from any to any keep state
+
+

After saving the configuration in /etc/pf.conf, reload the pf rules with:

+
pfctl -f /etc/pf.conf
+
+

To save rebooting and have MeshCentral launch then, launch it so you can begin using it. This time it is running as _meshcentral, now it is safe to keep running and you can use this command in the future.

+
cd /usr/local/meshcentral/node_modules/meshcentral/ && doas -u _meshcentral /usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral/meshcentral.js --launch
+
+

You can now access MeshCentral at https://youraddress:3000 or https://meshcentral.example.com:3000 if you named the machine meshcentral or create an A record named meshcentral. The first user you create will be the Administrator, there is no default user.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/intelamt/images/2022-05-16-23-08-15.png b/intelamt/images/2022-05-16-23-08-15.png new file mode 100644 index 00000000..f61fd2da Binary files /dev/null and b/intelamt/images/2022-05-16-23-08-15.png differ diff --git a/intelamt/images/2022-05-16-23-10-40.png b/intelamt/images/2022-05-16-23-10-40.png new file mode 100644 index 00000000..317ec314 Binary files /dev/null and b/intelamt/images/2022-05-16-23-10-40.png differ diff --git a/intelamt/images/2022-05-16-23-10-59.png b/intelamt/images/2022-05-16-23-10-59.png new file mode 100644 index 00000000..83254151 Binary files /dev/null and b/intelamt/images/2022-05-16-23-10-59.png differ diff --git a/intelamt/images/2022-05-16-23-11-24.png b/intelamt/images/2022-05-16-23-11-24.png new file mode 100644 index 00000000..7ee18b54 Binary files /dev/null and b/intelamt/images/2022-05-16-23-11-24.png differ diff --git a/intelamt/images/2022-05-16-23-12-04.png b/intelamt/images/2022-05-16-23-12-04.png new file mode 100644 index 00000000..c026ee3c Binary files /dev/null and b/intelamt/images/2022-05-16-23-12-04.png differ diff --git a/intelamt/images/2022-05-16-23-12-37.png b/intelamt/images/2022-05-16-23-12-37.png new file mode 100644 index 00000000..8c1a4f3e Binary files /dev/null and b/intelamt/images/2022-05-16-23-12-37.png differ diff --git a/intelamt/images/2022-05-16-23-12-58.png b/intelamt/images/2022-05-16-23-12-58.png new file mode 100644 index 00000000..b534a72a Binary files /dev/null and b/intelamt/images/2022-05-16-23-12-58.png differ diff --git a/intelamt/images/2022-05-16-23-13-44.png b/intelamt/images/2022-05-16-23-13-44.png new file mode 100644 index 00000000..11277a90 Binary files /dev/null and b/intelamt/images/2022-05-16-23-13-44.png differ diff --git a/intelamt/images/2022-05-16-23-14-42.png b/intelamt/images/2022-05-16-23-14-42.png new file mode 100644 index 00000000..f6e40f74 Binary files /dev/null and b/intelamt/images/2022-05-16-23-14-42.png differ diff --git a/intelamt/images/2022-05-16-23-15-04.png b/intelamt/images/2022-05-16-23-15-04.png new file mode 100644 index 00000000..e84ef62e Binary files /dev/null and b/intelamt/images/2022-05-16-23-15-04.png differ diff --git a/intelamt/images/2022-05-16-23-16-05.png b/intelamt/images/2022-05-16-23-16-05.png new file mode 100644 index 00000000..9614edc9 Binary files /dev/null and b/intelamt/images/2022-05-16-23-16-05.png differ diff --git a/intelamt/images/amtprovisioningserver.png b/intelamt/images/amtprovisioningserver.png new file mode 100644 index 00000000..fd64e618 Binary files /dev/null and b/intelamt/images/amtprovisioningserver.png differ diff --git a/intelamt/index.html b/intelamt/index.html new file mode 100644 index 00000000..986b0b25 --- /dev/null +++ b/intelamt/index.html @@ -0,0 +1,1873 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Intel AMT - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Intel AMT

+

Intel AMT Guide as .odt

+

Video Walkthru

+
+ + +
+ +

Abstract

+

This user guide contains all essential information for activating and using Intel® Active Management Technology (Intel® AMT) with MeshCentral. We will review how to activate, connect to and use Intel AMT features and how this benefit administrators that want to manage computers remotely. This document expect the reader to already be familiar with how to install and operate MeshCentral and have a basic understanding of how Intel® AMT works.

+

History of AMT

+
+ +
+ +

Introduction

+

MeshCentral is a free open source web-based remote computer management software and it fully supports Intel® Active Management Technology (Intel® AMT). MeshCentral does not require that computers it manages support Intel AMT, but if a remote computer has this capability, MeshCentral will make use of it.

+

Intel AMT can be seen as a hardware based management agent that is built into some Intel PC’s. Once setup, Intel AMT can be used to remotely manage a computer regardless of the operating system health. It can be used to power on a computer when it’s in soft-off state or to provide enhanced monitoring and security to remote systems.

+

Once setup, a computer can have up to management connections to MeshCentral. One of them by the Mesh Agent that lives in the operating system and another connection from Intel AMT. When remote management is made using an operating system agent, we call this “in-band management” and when management is done using a hardware based agent like Intel AMT, we call this “out-of-band management”

+

+

MeshCentral can support computers that have either or both agents. So, you can setup a computer with just the Mesh Agent, just Intel AMT or both. In this document we will show how to install computers with both agent connections or with just Intel AMT. When Intel AMT is used alone, we call this “agent-less” as there will be no operating system software required to remotely manage the computer.

+

The Mesh Agent and Intel® AMT have very different and complementary capabilities and so, it’s often beneficial to use both and one will offer features the other can’t provide. Here are some of the benefits each has to offer:

+

Mesh Agent

+
    +
  • Fast remote desktop / clipboard access.
  • +
  • Remote access to operating system files.
  • +
  • Remote chat and other OS features.
  • +
+

Intel® AMT

+
    +
  • Remote desktop even when the agent or operating system is not functional.
  • +
  • Remote access to BIOS.
  • +
  • Connectivity when soft-off / sleeping.
  • +
  • Remote power actions.
  • +
+

If you are looking into managing remote computers that would be difficult to physically get access to for remote support or maintenance, one should probably look at getting a PC with Intel AMT.

+

Bare-Metal Activation Server

+

The AmtProvisioningServer section in the settings section of the config.json will enable this feature. MeshCentral will then listen for activation requests, match against your ACM activation certificates and if everything goes well, will activate and add the device to a Intel AMT only device group. No agent or MeshCMD is involved.

+

This bare-metal activation server is not enabled by default and only makes sense when activating devices on the local network.

+

Once enabled, Intel AMT can send “hello” data to the MeshCentral provisioning server on port 9971 and MeshCentral will respond by connecting back, authenticating, and activating Intel AMT. MeshCentral will then log the event, add the device to a pre-defined agent-less device group and complete any remaining configuration. A trusted CA certificate is required to perform this operation fully automatically.

+

baremetal

+

MeshCentral Group Types

+

Once MeshCentral is installed, a user will typically create a new device group. Here is the first hint that MeshCentral supports Intel AMT. Device groups come in two types. You can manage using a software agent, or using Intel AMT only.

+

+

Note that if you use the OS agent to manage computers, you can also set and use Intel AMT. However, if you opt to create an Intel AMT only group, then Mesh Agents are not supported. One can create groups of both types in order to manage devices that have and don’t have the Mesh Agent installed.

+

+

The main benefit of “Intel AMT only” group is if someone does not want to install a background agent on remote systems or already have a remote management solution and intends to only use MeshCentral to supplement the existing solution with Intel AMT features.

+

Once a group is created, the links MeshCentral provides to on-board devices will change depending on the group type and how the server is setup. The device on-boarding links are located in the “My Devices” page, next to the group name.

+

+

If the MeshCentral server is setup in “LAN mode” or “Hybrid mode”, options will be available to add computers on the local network. If you have an Intel AMT computer that is already activated, you can select the “Add Local” or “Scan Network” options in the “Intel AMT only” group type and start adding local network computers this way. If MeshCentral is in “WAN mode”, you will need to setup Intel AMT to connect back to MeshCentral using a feature called “Client Initiated Remote Access” or CIRA for short. We will cover that in a later section.

+

Client Initiated Remote Access & MPS server

+

Client Initiated Remote Access (CIRA) is a feature of Intel AMT that, then configured, makes Intel AMT connect back to the server using a TLS tunneling connection similar with a SSH tunnel. Once this tunnel connection is established, the server can perform remote management operations on Intel AMT.

+

CIRA is great when remotely managing Intel AMT devices over the Internet thru network address translator (NAT) routers where the server would not be able to connect to Intel AMT. This is similar to the Mesh Agent that initiated and keeps an idle connection to the server.

+

By default, MeshCentral will be configured to receive Mesh Agent connections on TCP port 443 and Intel AMT connections on TCP port 4433. These port values can be configured in the config.json file of MeshCentral.

+

+

Once connected to port 443, the Mesh agent will using secure HTTPS WebSocket to securely communicate with the server. Intel AMT will use TLS to connect to port 4433 and use a binary tunneling protocol called the Intel AMT Port Forwarding Protocol (APF). You can find documentation on this protocol at the following URL:

+

https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/MPSDocuments/Intel%20AMT%20Port%20Forwarding%20Protocol%20Reference%20Manual.pdf

+

It’s not necessary to know or understand the details of this protocol, MeshCentral will take care of handling this. In Intel AMT nomenclature, the server that receives a CIRA connection is called a “Management Presence Server” or MPS for short. In other words, MeshCentral has a MPS server on port 4433 ready to receive Intel AMT CIRA connections.

+

+

When MeshCentral is first setup, a self-signed root certificate is created along with a MPS certificate that will be presented when a device connects on port 4433. There is typically no need to use a CA signed & trusted certificate on port 4433 was we only expect Intel AMT computers to connect to this port and we will be loading our self-signed root in Intel AMT for authentication purposes.

+

One way to check that the MeshCentral MPS server is running correctly is to use a browser and access port 4433 using HTTPS. The browser will display a warning because the port 4433 certificate is not trusted, but this is expected.

+

+

The CIRA protocol is binary, but MeshCentral will detect that the request is made from a browser and return a short message:

+
MeshCentral2 MPS server.
+Intel® AMT computers should connect here.
+
+

This is practical to make sure connectivity with the MeshCentral MPS server is working. Now that we know the basics of Intel AMT CIRA and the MPS server, we can configure Intel AMT to connect.

+

Activation Certificate Setup

+

If you have an Intel AMT activation certificate, you should configure MeshCentral to take advantage of it. Your activation certificate must have been issued by one of the certificate authorities (CA’s) that is trusted by Intel AMT and MeshCentral will need the entire certificate chain to be provided since the entire chain is needed to perform Intel AMT ACM activation.

+

+

The leaf certificate will have the Intel AMT activation option and a specific domain name while the hash of the trusted CA certificate must be trusted by Intel AMT. The certificate chain will have to be setup in the domain section of the MeshCentral config.json file.

+

If you have a certificate chain in a .pfx or .p12 format, place that file in the “meshcentral-data” folder and add the “AmtAcmActivation” section in the domain section like so:

+
{
+  "settings": {
+    "Cert": "devbox.mesh.meshcentral.com",
+  },
+  "domains": {
+    "": {
+      "title": "My Server",
+      "AmtAcmActivation": {
+        "log": "amtactivation.log",
+        "certs": {
+          "myamtcert": {
+            "certpfx": "amtcert.pfx",
+            "certpfxpass": "pfxpassword"
+          }
+        }
+      }
+    }
+}
+
+

If you have the certificate chain in PEM format as a set of .crt files and a .key file, start by placing all of the certificate files in the “meshcentral-data” folder and setup the certificate chain like this:

+
{
+  "settings": {
+    "Cert": "devbox.mesh.meshcentral.com",
+  },
+  "domains": {
+    "": {
+      "title": "My Server",
+      "AmtAcmActivation": {
+        "log": "amtactivation.log",
+        "certs": {
+          "myvprocert": {
+            "certfiles": [ "amtacm-vprodemo.crt",
+   "amtacm-intermediate1.crt",
+   "amtacm-intermediate2.crt",
+   "amtacm-root.crt" ],
+            "keyfile": "amtacm-vprodemo.key"
+          }
+        }
+      }
+    }
+}
+
+

It’s important that the leaf certificate file be the first file in the “certfiles” array. The order of the other certificates is not important as MeshCentral will figure out and re-order them correctly.

+

Within the new “AmtAcmActivation” section, there is a “log” entry. This is a log file that will contain every activation attempt including the details of the computer being activation and what Intel AMT administrator password was used for activation. This log file should be kept securely as it will contain Intel AMT credentials. It’s also important to have this file as a backup so that Intel AMT credentials are not lost after activation. If MeshCentral can’t write to this log, the activation will not go forward and will fail.

+

Once the config.json was modified, restart the server. There will be two indications that the server has the new certificate correctly configured. For “Intel AMT only” groups, a new “Activation” link will show up. Clicking this link will show a command that can be run to perform ACM activation.

+

+

For device groups that operate with a Mesh Agent, you can edit the group and select the “Simple Admin Control Mode” Intel AMT activation policy. This policy is not available unless a correct Intel AMT ACM activation certificate is configured.

+

+

Once setup, Intel AMT will not automatically activate to Intel AMT unless the right situation is met. The Intel AMT activation certificate is for a specific domain name suffix, for example “meshcentral.com”. Intel AMT must be in a situation where this domain can be accepted. One of the following must be true:

+
    +
  • Intel AMT must have a wired Ethernet interface that is connected to a local network with a DHCP server responding with option 15 set to “xxx.meshcentral.com”.
  • +
  • The name “meshcentral.com” by have been set as “Trusted FQDN” in MEBx.
  • +
  • The name “meshcentral.com” must have been set using a USB key with a setup.bin file.
  • +
+
+ +
+ +

Once Intel AMT is in a situation where ACM activation can occur, the activation command line can be run or the Mesh Agent will detect this situation and ask the server to perform activation.

+

+

The best way to test this feature is to create an “Intel AMT only” device group and run the MeshCMD command on the remote system to perform activation. If there is a problem, this process should clearly display why ACM activation fails.

+
+

Note

+
+

Activation over wifi has some additional issues.
+ First you need to add your WiFi access point to that wifi configuration to allow CSME to take over WiFi when OS is not functioning. Then it should work.
+ Please also make sure you install Intel WiFi driver and Intel LMS package. It should work. You can detach the ethernet and then try connecting to that device using the IP address acquired by WiFi interface. + See Open AMT Cloud Toolkit project - a close relative to this project. It has an AMT activation component and newer remote provisioning client can activate locally and also can manage Wi-Fi profile.

+

Intel AMT MEI and LMS

+

Intel Active Management Technology (Intel AMT) can communicate to the local platform using the Management Engine Interface (MEI). We show how your can use that to get Intel AMT information. For more advanced usages, you need to connect using TCP and TLS which requires Intel Local Manageability Service (LMS). We show how MeshCentral's Mesh Agent and MeshCMD have a small version of LMS built-in and how it works

+
+ +
+ +

Intel AMT System Defense

+

As part of Intel AMT there are hardware filters in the network interface you can setup to match and perform actions on packets. This happens at Ethernet speeds with no slow down and independent of the OS.

+
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/SSLnletsencrypt/index.html b/meshcentral/SSLnletsencrypt/index.html new file mode 100644 index 00000000..cd2534b7 --- /dev/null +++ b/meshcentral/SSLnletsencrypt/index.html @@ -0,0 +1,1634 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SSL - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

SSL/Letsencrypt

+

MeshCentral supports SSL using self generated certs, your own certs or Letsencrypt

+

Enabling letsencrypt

+

Make sure you match and/or adjust all the following settings appropriately in your config.json file:

+
{
+    "settings": {
+        "redirPort"
+        "cert": "yourdomain.com"
+    },
+    "domains": {
+        "letsencrypt": {
+        "__comment__": "Requires NodeJS 8.x or better, Go to https://letsdebug.net/ first before trying Let's Encrypt.",
+        "email": "myemail@myserver.com",
+        "names": "myserver.com,customer1.myserver.com",
+        "skipChallengeVerification": false,
+        "production": true
+        },
+    }
+}
+
+

If you need further clarification to know what each of these settings are, check out the config schema.

+

Then restart meshcentral and it will get a cert for you, the process will need to restart to apply the cert.

+

Useful resources/troubleshooting

+

To check letsencrypt is working properly please use https://letsdebug.net/. We are using the HTTP-O1 challenge method with these instructions.

+

Also make sure you have port 80 open and pointing to your meshcentral server, IT WILL NOT WORK if port 80 isn't open and it HAS to be port 80.

+

You can read more about Letsencrypt and meshcentral here.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/agents/index.html b/meshcentral/agents/index.html new file mode 100644 index 00000000..3fe5dd2c --- /dev/null +++ b/meshcentral/agents/index.html @@ -0,0 +1,1961 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Agent Information - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Mesh Agents

+

Windows

+

Default Install Path: c:\Program Files\Mesh Agent

+

Application Path: c:\Program Files\Mesh Agent\meshagent.exe

+

Application database Path: c:\Program Files\Mesh Agent\meshagent.db

+

Application Log Path: c:\Program Files\Mesh Agent\meshagent.log

+

xxx Path: c:\Program Files\Mesh Agent\meshagent.msh

+
+
+
+
    +
  • Start: net start "mesh agent"
  • +
  • Stop: net stop "mesh agent"
  • +
  • Restart: net restart "mesh agent"
  • +
  • Status: Needs info
  • +
+
+
+

Troubleshooting steps: Needs info

+
+
+
+

Linux / BSD

+

Uninstall: sudo /usr/local/mesh_services/meshagent/[agent-name]/meshagent -fulluninstall

+

Apple macOS Binary Installer

+

Default Install Path: /usr/local/mesh_services/meshagent/meshagent

+

Launches from /Library/LaunchAgents/meshagent.plist

+

Controlling agent

+
launchctl stop meshagent
+launchctl start meshagent
+
+

Install:

+

Uninstall: sudo /usr/local/mesh_services/meshagent/[agent-name]/meshagent -fulluninstall

+

Apple macOS Universal

+

For OSx 11+ including Big Sur, Monterey and later

+

Apple macOS

+

For macOS 10.x including Catalina, Mojave, High Sierra, Sierra, El Capitan, Yosemite, Mavericks, Mountain Lion and earlier.

+

Mobile Device (Android)

+

MeshCentral Assistant

+

See Assistant

+

Apple MacOS Binary Installer

+

Agent Commands

+

agentmsg +: Add/Remove badged messages to the device's web ui +

  agentmsg add "[message]" [iconIndex]
+  agentmsg remove [index]
+  agentmsg list
+
+agentsize +: Returns the binary size of the agent

+

agentupdate +: Manually trigger an agent self-update

+

alert +: Display an alert dialog on the logged in session +

alert TITLE, CAPTION [, TIMEOUT]
+

+

amt

+

amtconfig

+

amtevents

+

apf

+

args

+

av +: Displays Antivirus State

+

coredump

+

coreinfo

+

cpuinfo

+

cs +: Display Windows Connected Standby State

+

dbcompact +: Compacts the agent database

+

dbget

+

dbkeys

+

dbset

+

dnsinfo +: Display DNS server info

+

domain +: Display domain metadata

+

errorlog

+

eval +: executes javascript on the agent +

eval [code]
+

+

fdcount +: Returns the number of active descriptors in the event loop

+

fdsnapshot +: Returns detailed descriptor/handle/timer metadata

+

getclip +: Fetches clipboard data from agent

+

getscript

+

help +: Returns the list of supported console commands

+

httpget

+

info +: Returns general information about the agent, such as connected state, loaded modules, LMS state, etc

+

kill +: Sends a SIGKILL signal to the specified PID +

kill [pid]
+

+

kvmmode +: Displays the KVM Message Format

+

location +: Displays saves location information about the connected agent

+

lock

+

log +: Writes a message to the logfile +

log [message]
+

+

ls +: Enumerates the files in the agent's install folder

+

mousetrails +: Enables/Disables Mouse Trails Accessibility on Windows. To change setting, specify a positive integer representing the number of latent cursors, where 0 is disable +

mousetrails [n]
+

+

msh +: Displays the loaded msh settings file

+

netinfo +: Displays network interface information

+

notify +: Display a notification on the web interface

+

openurl

+

osinfo +: Displays OS information

+

parseuri +: Parses the specified URI, and displays the parsed output +

parseuri [uri]
+

+

plugin +: Invokes a plugin +

plugin [pluginName] [args]
+

+

power +: Performs the specified power action +

power [action]
+  LOGOFF = 1
+  SHUTDOWN = 2
+  REBOOT = 3
+  SLEEP = 4
+  HIBERNATE = 5
+  DISPLAYON = 6
+  KEEPAWAKE = 7
+  BEEP = 8
+  CTRLALTDEL = 9
+  VIBRATE = 13
+  FLASH = 14
+

+

print

+

privacybar +: Sets/Gets the default pinned state of the Privacy Bar on windows +

privacybar [PINNED|UNPINNED]
+

+

ps +: Enumerates processes on the agent

+

rawsmbios +: Fetches the raw smbios table

+

safemode +: Sets/Gets the SAFEMODE configuration of the agent, as well as the next boot state. +

safemode (ON|OFF|STATUS)
+

+

scanwifi +: Scans the available Wifi access points, and displays the SSID and Signal Strength

+

service +: Shortcut to be able to restart the agent service +

service status|restart
+

+

setclip +: Sets clipboard data to the agent +

setclip [text]
+

+

setdebug +: Sets the location target for debug messages +

setdebug [target]
+0 = Disabled
+1 = StdOut
+2 = This Console
+* = All Consoles
+4 = WebLog
+8 = Logfile
+

+

smbios +: Displays the parsed SMBIOS metadata

+

startupoptions +: Displays the command-line options that the agent was started with

+

sysinfo +: Collects and displays telemetry on the platform

+

task

+

taskbar +: Hides or shows the Windows System task bar, optionally on the specified Terminal Server Session ID +

taskbar HIDE|SHOW [TSID]
+

+

timerinfo +: Displays metadata about any configured timers on the event loop

+

toast +: Displays a toast message on the logged in user's session +

toast [message]
+

+

translations +: Shows the currently configured translations

+

type +

type (filepath) [maxlength]
+

+

uac +: Get/Sets the Windows UAC mode +

uac [get|interactive|secure]
+

+

unzip +

unzip input, destination
+
+: Unzips the specified file

+

users +: Enumerates the logged in users on the system

+

versions +: Displays version information about the agent

+

vm +: Detects if the system is a Virtual Machine

+

volumes +: Displays volume information reported by the OS

+

wakeonlan +: Sends wake-on-lan packets to the specified MAC address +

wakeonlan [mac]
+

+

wallpaper +: Gets/Toggles the logged in user's desktop background image +

wallpaper (GET|TOGGLE)
+

+

wpfhwacceleration +: Enable/Disable WPF HW Acceleration on Windows +

wpfhwacceleration (ON|OFF|STATUS)
+

+

wsclose

+

wsconnect

+

wslist

+

wssend

+

zip +

zip (output file name), input1 [, input n]
+

+

Agent msh options

+

You can find a full list of options for the agent here

+

skipmaccheck=1: Will not regenerate the agents nodeid and cause duplication of the agent when the MAC address changes.

+

You can add options to your .msh on agent install with this

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/assistant/index.html b/meshcentral/assistant/index.html new file mode 100644 index 00000000..29618aca --- /dev/null +++ b/meshcentral/assistant/index.html @@ -0,0 +1,1694 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Assistant - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

MeshCentral Assistant

+

Initial Setup

+

Agent Invite Code

+
"domains": {
+    "": {
+        "agentInviteCodes": true
+    }
+}
+
+

agent invite code

+

Agent Invitation

+

Click on the 'Invite' button next to the device group name to access it.

+ +

For link invitation web page customization:

+
    +
  1. Alongside meshcentral-data create a folder called meshcentral-web
  2. +
  3. Create a views folder in it and copy the file node_modules/meshcentral/views/invite.handlebars into it.
  4. +
  5. That copy will be served instead of the default one, so you can customize it as you want.
  6. +
+

agent invite code

+

Email Invitation

+

This option will show up if you have an SMTP email server set up with MeshCentral.

+

For invitation email customization:

+
    +
  1. Alongside meshcentral-data create a folder called meshcentral-web
  2. +
  3. Create an emails folder in it and copy the files node_modules/meshcentral/emails/mesh-invite.txt and node_modules/meshcentral/emails/mesh-invite.html into it.
  4. +
  5. These copies will be used instead of the default ones, so you can customize them as you want.
  6. +
+

email-invitation

+

Email notification

+

You can also get an email notification when someone clicks the "Request Help" button in the Assistant agent.

+

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/codesigning/index.html b/meshcentral/codesigning/index.html new file mode 100644 index 00000000..cd95fab0 --- /dev/null +++ b/meshcentral/codesigning/index.html @@ -0,0 +1,1681 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code Signing - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Code Signing

+ +

Authenticode-JS Video

+

Nodejs Code Signing module

+
+ +
+ +

MeshCentral comes with authenticode.js, you can run it like this:

+
node node_modules/meshcentral/authenticode-js
+
+

and you will get

+
MeshCentral Authenticode Tool.
+Usage:
+  node authenticode.js [command] [options]
+Commands:
+  info: Show information about an executable.
+        --exe [file]             Required executable to view information.
+        --json                   Show information in JSON format.
+  sign: Sign an executable.
+        --exe [file]             Required executable to sign.
+        --out [file]             Resulting signed executable.
+        --pem [pemfile]          Certificate & private key to sign the executable with.
+        --desc [description]     Description string to embbed into signature.
+        --url [url]              URL to embbed into signature.
+        --hash [method]          Default is SHA384, possible value: MD5, SHA224, SHA256, SHA384 or SHA512.
+        --time [url]             The time signing server URL.
+        --proxy [url]            The HTTP proxy to use to contact the time signing server, must start with http://
+  unsign: Remove the signature from the executable.
+        --exe [file]             Required executable to un-sign.
+        --out [file]             Resulting executable with signature removed.
+  createcert: Create a code signging self-signed certificate and key.
+        --out [pemfile]          Required certificate file to create.
+        --cn [value]             Required certificate common name.
+        --country [value]        Certificate country name.
+        --state [value]          Certificate state name.
+        --locality [value]       Certificate locality name.
+        --org [value]            Certificate organization name.
+        --ou [value]             Certificate organization unit name.
+        --serial [value]         Certificate serial number.
+  timestamp: Add a signed timestamp to an already signed executable.
+        --exe [file]             Required executable to sign.
+        --out [file]             Resulting signed executable.
+        --time [url]             The time signing server URL.
+        --proxy [url]            The HTTP proxy to use to contact the time signing server, must start with http://
+  icons: Show the icon resources in the executable.
+        --exe [file]               Input executable.
+  saveicons: Save an icon group to a .ico file.
+        --exe [file]               Input executable.
+        --out [file]               Resulting .ico file.
+        --icongroup [groupNumber]  Icon groupnumber to save to file.
+        --removeicongroup [number]
+        --icon [groupNumber],[filename.ico]
+
+Note that certificate PEM files must first have the signing certificate,
+followed by all certificates that form the trust chain.
+
+When doing sign/unsign, you can also change resource properties of the generated file.
+
+          --filedescription [value]
+          --fileversion [value]
+          --internalname [value]
+          --legalcopyright [value]
+          --originalfilename [value]
+          --productname [value]
+          --productversion [value]
+
+

Automatic Agent Code Signing

+

If you want to self-sign the mesh agent so you can whitelist the software in your AV, as well as lock it to your server and organization:

+
+ +
+ +
+

Note

+

If you generate your private key on windows with use BEGIN PRIVATE KEY and openssl needs BEGIN RSA PRIVATE KEY you can convert your private key to rsa private key using openssl rsa -in server.key -out server_new.key

+
+

Setting Agent File info

+

Now that MeshCentral customizes and signs the agent, you can set that value to anything you like.

+
"domains": {
+      "agentFileInfo": {
+            "filedescription": "sample_filedescription",
+            "fileversion": "0.1.2.3",
+            "internalname": "sample_internalname",
+            "legalcopyright": "sample_legalcopyright",
+            "originalfilename": "sample_originalfilename",
+            "productname": "sample_productname",
+            "productversion": "v0.1.2.3"
+      }
+}
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/config/index.html b/meshcentral/config/index.html new file mode 100644 index 00000000..28a25ed6 --- /dev/null +++ b/meshcentral/config/index.html @@ -0,0 +1,1542 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All Configuration Options - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Configuration Options

+

There are MANY configuration options available with meshcentral, search this file for options: https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json

+

Some options you can find relate to:

+
    +
  • DNS
  • +
  • HTTPS
  • +
  • MPS (Management Presence Server)
  • +
  • MongoDB
  • +
  • MariaDB
  • +
  • SQLite3
  • +
  • MySQL
  • +
  • PostgreSQL
  • +
  • AceBase
  • +
  • WAN (Wide Area Network)
  • +
  • LAN (Local Area Network)
  • +
  • Maintenance Mode
  • +
  • Session Cookie
  • +
  • Database Encryption
  • +
  • Web Relay
  • +
  • Agent Connection
  • +
  • TLS (Transport Layer Security)
  • +
  • WebRTC
  • +
  • Web Push Notifications
  • +
  • Auto Backup
  • +
  • Crowdsec
  • +
  • IP KVM (Keyboard, Video, Mouse over IP)
  • +
  • Mesh Router
  • +
  • Syslog
  • +
  • WebDAV
  • +
  • Certificates and Authentication
  • +
  • MeshCentral Server Settings
  • +
  • Device Management
  • +
  • User Permissions
  • +
  • Remote Desktop Configuration
  • +
+

and more!

+

Server Options

+

Domain Options

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/customization/index.html b/meshcentral/customization/index.html new file mode 100644 index 00000000..d778049a --- /dev/null +++ b/meshcentral/customization/index.html @@ -0,0 +1,1718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customization - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Customization

+

Whitelabeling your MeshCentral installation to personalize it to your company's brand, as well as having your own terms of use is one of the first things many people do after installation.

+
+ +
+ +

Web Branding

+

You can put your own logo on the top of the web page. To get started, get the file “logoback.png” from the folder “node_modules/meshcentral/public/images” and copy it to your “meshcentral-data” folder. In this example, we will change the name of the file “logoback.png” to “title-mycompany.png”. Then use any image editor to change the image and place your logo.

+

+

Once done, edit the config.json file and set one or all of the following values:

+
"domains": {
+  "": {
+    "Title": "",
+    "Title2": "",
+    "TitlePicture": "title-sample.png",
+    "loginPicture": "logintitle-sample.png",
+    "welcomeText": "This is sample text",
+    "welcomePicture": "mainwelcome-04.jpg",
+    "welcomePictureFullScreen": true,
+    "siteStyle": "1",
+    "nightMode": "1",
+    "meshMessengerTitle": "Mesh Chat",
+    "meshMessengerPicture": "chatimage.png",
+    "footer": "This is a HTML string displayed at the bottom of the web page when a user is logged in.",
+    "loginfooter": "This is a HTML string displayed at the bottom of the web page when a user is not logged in."
+  },
+
+

This will set the title and sub-title text to empty and set the background image to the new title picture file. You can now restart the server and take a look at the web page. Both the desktop and mobile sites will change.

+

+

+

The title image must a PNG image of size 450 x 66.

+

You can also customize the server icon in the “My Server” tab. By default, it’s a picture of a desktop with a padlock.

+

+

If, for example, MeshCentral is running on a Raspberry Pi. You may want to put a different picture at this location. Just put a “server.jpg” file that is 200 x 200 pixels in the “meshcentral-data” folder. The time MeshCentral page is loaded, you will see the new image.

+

+

This is great to personalize the look of the server within the web site.

+

Customizing Web Icons

+

MeshCentral lets you change the icons for different devices shown in the Web User Interface. To do this the proper way, you should make a new folder called meshcentral-web in the main directory, where you find other folders like meshcentral-data, meshcentral-backup, meshcentral-files, and node-modules. Inside meshcentral-web, make another folder named public and copy the entire node_modules/meshcentral/public/images folder into this new meshcentral-web/public folder and then edit the files in meshcentral-web/public/images/. This step is suggested because if MeshCentral updates, it might delete any changes in node_modules. But, changes in meshcentral-web will stay safe, and MeshCentral will use these files instead of the originals in node_modules.

+

To update device icons, you need to edit these files: meshcentral-web/public/images/webp/iconsXX.webp (icons16.webp, icons32.webp, icons50.webp, icons100.webp), and meshcentral-web/public/images/iconsXX.png (icons16.png, icons32.png, icons50.png, icons64.png, icons100.png) and the corresponding meshcentral-web/public/images/icons256-X-1.png. Make sure to keep the resolution of these files as it is.

+

By following these steps, you can customize any icon in MeshCentral. Just find and change the corresponding image files in the meshcentral-web/public/images folder. Similarly, you can also move other folders from node_modules/meshcentral to meshcentral-web while keeping the original folder structure. This allows you to modify other parts of MeshCentral too, like the .handlebars templates for the web interface. Simply copy files from node_modules/meshcentral/views to meshcentral-web/views and make your changes in meshcentral-web. This lets you match MeshCentral's look to your company's brand or your own style.
+

+

Customizing Agent Invitation

+

Agents can be invited by public link or via email. Click Here to see details.

+

Agent Branding

+

You can customize the Agent to add your own logo, change the title bar, install text, the service name, or even colors!

+
+

Note

+

The Customization must be done FIRST and BEFORE you deploy your agents! Once the agents have been deployed, any customization made afterwards, will not sync! This is because the setup files are customized on the fly, then when you install the agents, the exe and .msh file with the customizations in are copied over to the required folder, so you will need to reinstall the agent for agent customizations to take effect.

+
+

+
"domains": {
+    "": {
+        "agentCustomization": {
+            "displayName": "MeshCentral Agent",
+            "description": "Mesh Agent background service",
+            "companyName": "Mesh Agent Company",
+            "serviceName": "Mesh Agent Service",
+            "installText": "Text string to show in the agent installation dialog box",
+            "image": "mylogo.png",
+            "fileName": "meshagent",
+            "foregroundColor": "#FFA500",
+            "backgroundColor": "#EE82EE"
+        }
+    }
+}
+
+

agent icon

+

Terms of use

+

You can change the terms of use of the web site by adding a “terms.txt” file in the “meshcentral-data” folder. The file can include HTML markup. Once set, the server does not need to be restarted, the updated terms.txt file will get used the next time it’s requested.

+

For example, placing this in “terms.txt”

+
<br />
+This is a <b>test file</b>.
+
+

Will show this on the terms of use web page.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/debugging/index.html b/meshcentral/debugging/index.html new file mode 100644 index 00000000..8f8acafd --- /dev/null +++ b/meshcentral/debugging/index.html @@ -0,0 +1,2217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Debugging - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Debugging

+ +

Websockets Video

+

Make sure you understand how MeshCentral works with your browser using chrome developer tools.

+
+ +
+ +

MeshCentral Server

+

Useful config.js settings

+

https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json

+
"AgentsInRAM": false,
+"AgentUpdateBlockSize": 2048,
+"agentUpdateSystem": 1,
+"noAgentUpdate": 1,
+"WsCompression": false,
+"AgentWsCompression": false,
+
+

Understanding node and paths

+

Note that when running MeshCentral, you should always run from the path that is parent to node_modules, so you do this:

+
cd C:\Program Files\Open Source\MeshCentral
+node node_modules\meshcentral
+
+

You do NOT do this:

+
cd C:\Program Files\Open Source\MeshCentral\node_modules\meshcentral
+node meshcentral
+
+

The problem with the second command is that NPM may install missing modules in the incorrect location.

+

Also, in general I recommend not using the MeshCentral MSI Installer and just install manually unless you are very scared of the command prompt. Anyone that knows a bit about the shell should install MeshCentral like this:

+
mkdir c:\meshcentral
+cd c:\meshcentral
+npm install meshcentral
+node node_modules\meshcentral
+(ctrl-c when done installing optional modules)
+node node_modules\meshcentral --install
+
+

This way, you have a lot more control over what is going on. In my opinion, the MSI installer basically does the same thing and installs NodeJS for you.

+

Unable to update server

+

Generally the problem is that MeshCentral can't find the npm tool and therefore, can't run it to see if there is a new version. You can fix this by setting the path to npm in the config.json like this:

+
{
+  "settings": {
+    "npmPath": "c:\\npm.exe",
+    "npmProxy": "http://1.2.3.4:80"
+  }
+}
+
+

The problem could also be that you need a proxy, the configuration line to that is above.

+

You can also manually update. Just stop your server and so this:

+
mv node_modules node_modules_bak
+npm install meshcentral
+node node_modules/meshcentral
+
+

Then wait for all optional modules to install, then once the server starts hit ctrl-c and start up the server again. You can also use the following to help you start/stop the server:

+
node node_modules/meshcentral --install
+node node_modules/meshcentral --uninstall
+node node_modules/meshcentral --start
+node node_modules/meshcentral --stop
+
+

Port Troubleshooting on server

+

If you're getting a port 4433 is not available error, this is because another process is using this port, very likely another instance of MeshCentral. If your MeshCentral server is bound to ports 81/444 MeshCentral could not get port 80/443 and got the next available ones.

+

In general the problem is that you are running two MeshCentral instances at the same time. Probably one as a background Windows Service and one in the command line. Which ever instance can grab port 4433 will have a running MPS and CIRA should work, but the second instance will not have port 4433 and CIRA will not work.

+

Running Meshcentral server in debug mode

+

Debug more will cause MeshCentral to output a lot of debug messages to the console. To display all debug messages, run MeshCentral like this:

+
node node_modules/meshcentral --debug
+
+

A more practical way to run the debug command it to specify what messages you want printed out using a comma seperated list, for example:

+
node node_modules/meshcentral --debug web,amt,mps
+
+

Here is the list of all debug options:

+
cookie       - Cookie encoder
+dispatch     - Message Dispatcher
+main         - Main Server Messages
+peer         - MeshCentral Server Peering
+agent        - MeshAgent traffic
+agentupdate  - MeshAgent update
+cert         - Server Certificate
+db           - Server Database
+email        - Email/SMS/Push Traffic
+web          - Web Server
+webrequest   - Web Server Requests
+relay        - Web Socket Relay
+httpheaders  - Web Server HTTP Headers
+authlog      - User Authentication Log
+amt          - Intel AMT
+webrelay     - Connection Relay
+mps          - CIRA Server
+mpscmd       - CIRA Server Commands
+
+

You can also specify the debug option in the config.json file in the settings section. For example:

+
"settings": {
+  "debug": "web,amt,mps"
+}
+
+

Enabling trace in your browser Dev Tools

+

You can enable browser console tracing by adding trace=1 as a parameter to the URL of the MeshCentral main web page. For example https://myserver.com/?trace=1. Once present, open the browser's console window to see all web client tracing messages.

+

To log all database queries, change log_statement in /etc/postgresql/13/main/postgresql.conf

+
# CUSTOM
+log_statement = 'all'           # none, ddl, mod, all
+
+

The stacktrace was logged to stdout/journalctl. Supposedly, you can enable debug logging for node modules by adding DEBUG=<modulename> to the environment.

+

Adding this to /etc/systemd/system/meshcentral.service should do it but it didn't seem to do anything.

+

I think that's because Mesh uses the trace logging in the browser instead of logging things in the server logs.

+
Environment=DEBUG=mesh*
+
+

If you want to change node to meshcentral in journalctl, add this to /etc/systemd/system/meshcentral.service.

+
SyslogIdentifier=meshcentral
+
+

Finding agent problems

+

Using the servers My Server > console

+
    +
  • agentstatus - Gives you summary counts of problems
  • +
  • agentissues - Gives IP:port and what error
  • +
  • dupagents - Gives duplicateAgent IP's, counts and names
  • +
+

Logging it all

+

To log everything that's possible, prepare the log directory.

+
mkdir /meshcentral/meshcentral-logs/
+chown tactical:tactical logs
+ln -s ../meshcentral-logs/log.txt /meshcentral/meshcentral-data/log.txt
+
+

And then add this to your config.

+
    "meshErrorLogPath": "/meshcentral/meshcentral-logs/",
+    "authLog": "/meshcentral/meshcentral-logs/auth.log",
+    "log": [
+        "cookie",
+        "dispatch",
+        "main",
+        "peer",
+        "web",
+        "webrequest",
+        "relay",
+        "webrelaydata",
+        "webrelay",
+        "mps",
+        "mpscmd",
+        "swarm",
+        "swarmcmd",
+        "agentupdate",
+        "agent",
+        "cert",
+        "db",
+        "email",
+        "amt",
+        "httpheaders",
+        "websocket"
+    ],
+
+

You'll then have 3 files:

+
-rw-rw-r-- 1 tactical tactical   2593 Feb  2 12:22 auth.log
+-rw-r--r-- 1 tactical tactical 147593 Feb  2 12:31 log.txt
+-rw-rw-r-- 1 tactical tactical    381 Feb  2 12:02 mesherrors.txt
+
+

log.txt will now log everything in the Trace tab

+

Restricting server to specific IP(s)

+

When doing debugging on my development server, I use this line in the settings section to block all agent connections except the agent I want:

+
"agentAllowedIp": [ "192.168.2.147" ],
+
+

Of course, this is just for debugging.

+

Finding system ID types

+

https://serverurl/meshagents aka trying figure out what this is

+

ID

+

Pull down cert .crt file from internet

+

See #1662 We have run into this challenge before, where our .crt file expired and then all our agents were unable to connect. In our case, the TLS cert was available on the internet, and thus, we were able to use these commands to update it:

+
echo -n \| openssl s_client -connect yourdomain.com:443 2> /dev/null\| sed -ne '/-BEGIN   CERTIFICATE-/,/-END CERTIFICATE-/p' > /opt/meshcentral/meshcentral-data/webserver-cert-public.crt
+service meshcentral restart
+
+

MeshAgent

+

Troubleshooting agent

+
./meshagent -state
+
+

Agent Debug Logs to server

+

This automatically downloads all agent error logs into meshcentral-data/agenterrorlogs.txt

+

Set in config.json

+
"agentLogDump": true
+
+

Determine Agent capabilities

+

On the server goto the agents console tab. Type:

+
info
+
+

Useful MeshAgent.msh flags

+

https://github.com/Ylianst/MeshAgent/blob/master/meshcore/agentcore.h#L190

+
controlChannelDebug=1
+logUpdate=1
+
+

Obtain generated .msh File

+

If you need a trick to get the .msh file, you can add ?debug=1 to the URL and click "Add Agent", there will be an extra link to download it.

+

MeshAgent Commands

+
MeshAgent run
+MeshAgent dbTool.js list
+
+

Forcing Core version from Cmdline

+
    +
  • Download meschore.js and rename to CoreModule.js and put it alongside MeshAgent.exe
  • +
  • Stop MeshAgent service
  • +
  • Run MeshAgent.exe dbTool.js import CoreModule
  • +
+

On the fly Patching MeshAgent

+

MeshAgent#89 (comment)

+

There are two ways to do this... When debugging, and making changes, you can modify the .js file directly, and just save it in the same folder as the agent binary... The agent will use the .js file from disc if it's there, if it's newer than the one compiled in the binary. You don't even need to restart the agent. You can just clear the core, and reload the core.....

+

When you are satisfied with your changes to the .js file, you can use the clipboard, in the following fashion:

+
meshagent -exec "require('clipboard').nativeAddCompressedModule('foo');process.exit();"
+
+

if the file you modified isn't in the same folder as the agent binary, you can use the following command if you don't want to move the file, and edit it directly in the modules folder:

+
meshagent -exec "setModulePath('pathToFolder');require('clipboard').nativeAddCompressedModule('foo');process.exit();"
+
+

This command is just like the previous, except it searches for modules in the path specified.

+

Just substitute foo, with the name of the module that you modified. It will load the module from disc, compress it, and save it into the clipboard.. So you can just load up your editor for ILibDuktape_Polyfills.c, and find where that particular module is defined... and paste directly from the clipboard... The clipboard will contain all the necessary C code to uncompress and load the module.

+

If the compressed result is relatively long, it will auto break it up into multiple lines to work around an issue with visual studio's maximum string literal limitations.

+

Agent Debugging using MeshCore JS Debugger

+

(#119) How to test changes to the meshagent and recompile them.

+
    +
  • Copy duktape-debugger.js to the mesh directory on the target machine.
  • +
  • From the console tab of the agent, enter this command, substituting the port number you want to use instead of 9999 +eval "attachDebugger({ webport: 9999 })"
  • +
+

Then open your browser to http://localhost:9999 or whatever port you used.

+
+

Note

+

If you pause the debugger, and happen to forget about it, the agent will automatically kill itself and restart because it will think that a thread is stuck. Default debugger timeout is 10 minutes, you may find a log entry saved to disk saying "Microstack Thread STUCK", or something similar.

+
+

Troubleshooting Agent connectivity

+

If an agent keeps disconnecting and reconnecting, add this line to the "settings" section of the config.json:

+
"agentping": 30
+
+

This will cause MeshCentral to "ping" the agent every 30 seconds and the agent to respond with a "pong" each time. That usually solves the issue however, it does generate more traffic. If that works, you can remove the line and try this line instead:

+
"agentpong": 30
+
+

This will cause MeshCentral to "pong" the agent every 30 seconds, the agent will not respond. This usually fixes the issue, but you have half the traffic. I would also increase the time like:

+
"agentpong": 90
+
+

This is the best, you have one way traffic to all agents every 90 seconds. The larger the number you can get away with the better.

+

If you ever get the same problem but on the browser side, you can also use one of these:

+
"browserping": 30
+"browserpong": 30
+
+

Same idea, browser side instead of agent side.

+

Intel AMT

+

To debug issues, confirm that Intel AMT is active and there MeshCentral is not showing any red errors on the "Intel AMT" line:

+

+

Next, you can go in the "My Server / Trace" tab and enable tracing on the "Intel AMT Manager" like this:

+

+

You can then open another tab and select to power off or power on Intel AMT, you should see "performPowerAction" with 2 or 8 depending on power on/off.

+

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/devicetabs/index.html b/meshcentral/devicetabs/index.html new file mode 100644 index 00000000..047733b6 --- /dev/null +++ b/meshcentral/devicetabs/index.html @@ -0,0 +1,2104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Device Tabs - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + +

Device Tabs

+

Search or Filter

+

You can search your list of agents using any of these criteria using the filter box (also viewable in the tooltip of the Filter box):

+

user:xxx or u:xxx +ip:xxx +group:xxx or g:xxx +tag:xxx or t:xxx +atag:xxx or a:xxx +os:xxx +amt:xxx +desc:xxx +wsc:ok +wsc:noav +wsc:noupdate +wsc:nofirewall +wsc:any

+

General

+

For viewing general information about the agent

+
    +
  • Group
  • +
  • Description
  • +
  • IP-KVM Port Number
  • +
  • IP-KVM Port Type
  • +
  • Intel AMT
  • +
  • Intel AMT Tag
  • +
  • Mesh Agent
  • +
  • Operating System
  • +
  • Windows Security
  • +
  • Antivirus
  • +
  • Active User
  • +
  • User Consent
  • +
  • Notifications
  • +
  • Tags
  • +
+

Fields

+

Buttons

+

Actions +Notes +Log Event +Message +PDU On/Off/Control +Chat

+

7 Day Power State

+

+

Legend

+
    +
  • Black color: Device is Powered On on (Intel AMT & agents)
  • +
  • Purple color: Device is in sleep state such as Hibernating (Intel AMT agents only)
  • +
  • Teal color: Device is connected through AMT/CIRA, but the Power State is UNKNOWN (Intel AMT agents only)
  • +
  • DarkGreen color: Device is connected through AMT/CIRA and is in Soft-Off Power State (Intel AMT agents only)
  • +
  • Grey color: Device is Powered Off/Not Connected To MeshCentral (Intel AMT & agents)
  • +
+ +

You can set alternate port in the link if it's not default by right-clicking.

+

+
    +
  • Interfaces
  • +
  • Location
  • +
  • MeshCmd
  • +
  • RDP
  • +
  • Web-VNC
  • +
  • Web-RDP
  • +
  • Web-SSH
  • +
  • XTerm
  • +
  • HTTP
  • +
  • HTTPS
  • +
  • SSH
  • +
  • SCP
  • +
  • MQTT Login
  • +
+

Desktop

+

For connecting to the machines KVM interface.

+

Connect Button

+

Right-clicking on Connect button will give you additional options:

+
    +
  • Ask Consent + Bar
  • +
  • Ask Consent
  • +
  • Privacy Bar
  • +
+

+

RDP Connect Button

+

Right-clicking on RDP Connect button allows you to specify Alternate Port.

+

+

Intel AMT Connect Button

+

Uses Intel AMT to control video output of the hardware video card.

+

During a desktop session

+

Bottom left include:

+

desktop bottom left

+
    +
  • Sending special Keys
  • +
+

Top Right include:

+

Actions

+

+
    +
  • Wake Up
  • +
  • Run Commands
  • +
  • Sleep
  • +
  • Reset
  • +
  • Power Off
  • +
  • Uninstall Agent
  • +
+

Settings

+

+
    +
  • Quality
  • +
  • Scaling
  • +
  • Frame rate
  • +
  • Swap Mouse Buttons
  • +
  • Reverse Mouse Wheel
  • +
  • Use Remote Keyboard Map
  • +
  • Automatic Clipboard
  • +
  • Lock on Disconnect
  • +
+

+
    +
  • Session recording indicator
  • +
  • Screen rotating
  • +
  • switching view mode
  • +
  • full screen
  • +
+

Bottom right include:

+

+
    +
  • Session Sharing with Guest
  • +
  • Toggling keyboard lockout on guest
  • +
  • Refresh Desktop view
  • +
  • Upload Clipboard
  • +
  • Download Clipboard
  • +
  • Record Session to file
  • +
  • Take screenshot
  • +
  • Toggle Remote Desktop Background
  • +
  • Open URL on remote desktop
  • +
  • Lock the Remote computer
  • +
  • Display a notification on the remote computer
  • +
  • Open Chat Window
  • +
+

Terminal

+

For connecting to a command line based interface on the agent

+

Right-clicking on Connect button allows you to:

+
+

Note

+

Linux and Windows have different options:

+
+
    +
  • Admin Shell (Windows)
  • +
  • Admin Powershell (Windows)
  • +
  • User Shell (Windows)
  • +
  • User Powershell (Windows)
  • +
  • SSH (Linux)
  • +
+

+

Files

+

For transferring files to and from the agent.

+

Events

+

Mesh Events related to the agent. This is your audit log to see what actions have been taken on the agent from the MeshCentral server.

+

Details

+

Agent information that includes:

+
    +
  • OS
  • +
  • Agent Info
  • +
  • Network Info
  • +
  • BIOS
  • +
  • Motherboard
  • +
  • Memory
  • +
  • Storage
  • +
  • Intel AMT
  • +
+

Note you can show CPU and Memory usage info by clicking the icon in the top right corner

+

+

Intel AMT

+

Console

+

For debugging and communicating with the mesh agent.

+

It allows JS commands to be issued to the device but also run extra commands from the meshcore. Type help for all available options

+
    +
  • 2falock
  • +
  • acceleratorsstats
  • +
  • agentissues
  • +
  • agentstats
  • +
  • amtacm
  • +
  • amtmanager
  • +
  • amtpasswords
  • +
  • amtstats
  • +
  • args
  • +
  • autobackup
  • +
  • backupconfig
  • +
  • bad2fa
  • +
  • badlogins
  • +
  • certexpire
  • +
  • certhashes
  • +
  • closeusersessions
  • +
  • cores
  • +
  • dbcounters
  • +
  • dbstats
  • +
  • dispatchtable
  • +
  • dropallcira
  • +
  • dupagents
  • +
  • email
  • +
  • emailnotifications
  • +
  • firebase
  • +
  • heapdump
  • +
  • heapdump2
  • +
  • help
  • +
  • info
  • +
  • le
  • +
  • lecheck
  • +
  • leevents
  • +
  • maintenance
  • +
  • migrationagents
  • +
  • mps
  • +
  • mpsstats
  • +
  • msg
  • +
  • nodeconfig
  • +
  • print
  • +
  • relays
  • +
  • removeinactivedevices
  • +
  • resetserver
  • +
  • serverupdate
  • +
  • setmaxtasks
  • +
  • showpaths
  • +
  • sms
  • +
  • swarmstats
  • +
  • tasklimiter
  • +
  • trafficdelta
  • +
  • trafficstats
  • +
  • updatecheck
  • +
  • usersessions
  • +
  • versions
  • +
  • watchdog
  • +
  • webpush
  • +
  • webstats
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/faq/index.html b/meshcentral/faq/index.html new file mode 100644 index 00000000..4ce7c3fc --- /dev/null +++ b/meshcentral/faq/index.html @@ -0,0 +1,1708 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FAQ - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

FAQ

+

json config files

+

Any item in the config.json file starting with an underscore character are ignored.

+

Ignored

+
"_title": "MyServer"
+
+

Valid setting

+
"title": "MyServer"
+
+

json requires correct formatting, if in doubt copy/paste your json config into a web based format checker to make sure you have it right: https://duckduckgo.com/?va=j&t=hc&q=json+lint&ia=answer

+

Help! I've been hacked there are weird agents appearing in my MeshCentral Console

+

No, you haven't.

+
    +
  1. +

    Your agent installer was scanned by an antivirus.

    +
  2. +
  3. +

    It didn't recognize the exe.

    +
  4. +
  5. +

    You have the option enabled to submit unknown applications for analysis.

    +

    AV Option1

    +
  6. +
  7. +

    They ran it against their virtualization testing cluster.

    +
  8. +
  9. +

    You allow anyone to connect to your server (you should look into techniques to hide your server from the internet).

    +
  10. +
  11. +

    Here are some examples of what that looks like.

    +
  12. +
+

Can't login on server after first setup

+

You're sure you're typing in everything right, giving it 2FA code and can't login

+

TOTP is time sensitive, check your time/NTP and make sure it's right (on server and TOTP app device)! :)

+

+

Branding and Customization

+

You can brand and customize MeshCentral almost as much as you like without delving into the code, a few changes in the config.json file and uploading images can change the way your system looks. Read more here

+
+

Note

+

You will need to reinstall the agent for agent customizations to take effect.

+
+

Mac Clients

+

You have to manually grant Mac permissions outside of the agent install process due to the MacOS security system under Security & Privacy > Privacy

+

To see the screen (otherwise you just see the menu bar, and otherwise blank)

+

+

To be able to transfer files

+

+

To be able to control keyboard and mouse

+

+

I'm using CloudFlare and I'm getting a black screen but the mouse moves?

+

If you are using CloudFlare for your DNS hosting and your remote screen is black, DONT PANIC!

+

Unfortunately, MeshCentral doesn't always work with CloudFlare's Proxy DNS Mode.

+

The fix is to simply set the 'Proxy Status' to OFF inside your DNS A Record, within the CloudFlare control panel.

+

Simply follow the steps here

+

Once done, open your firewall for the port and agentPort ports of where your meshcentral is hosted, then restart your MeshCentral Server

+

There is currently a PINNED GitHub issue about this here

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/images/2022-05-18-22-11-41.png b/meshcentral/images/2022-05-18-22-11-41.png new file mode 100644 index 00000000..374b56b1 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-11-41.png differ diff --git a/meshcentral/images/2022-05-18-22-16-11.png b/meshcentral/images/2022-05-18-22-16-11.png new file mode 100644 index 00000000..bc896d38 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-16-11.png differ diff --git a/meshcentral/images/2022-05-18-22-20-08.png b/meshcentral/images/2022-05-18-22-20-08.png new file mode 100644 index 00000000..2c62b23a Binary files /dev/null and b/meshcentral/images/2022-05-18-22-20-08.png differ diff --git a/meshcentral/images/2022-05-18-22-23-59.png b/meshcentral/images/2022-05-18-22-23-59.png new file mode 100644 index 00000000..4e8b54e6 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-23-59.png differ diff --git a/meshcentral/images/2022-05-18-22-25-39.png b/meshcentral/images/2022-05-18-22-25-39.png new file mode 100644 index 00000000..288eb904 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-25-39.png differ diff --git a/meshcentral/images/2022-05-18-22-26-14.png b/meshcentral/images/2022-05-18-22-26-14.png new file mode 100644 index 00000000..360665b5 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-26-14.png differ diff --git a/meshcentral/images/2022-05-18-22-27-25.png b/meshcentral/images/2022-05-18-22-27-25.png new file mode 100644 index 00000000..5cb6b725 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-27-25.png differ diff --git a/meshcentral/images/2022-05-18-22-29-12.png b/meshcentral/images/2022-05-18-22-29-12.png new file mode 100644 index 00000000..2db41657 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-29-12.png differ diff --git a/meshcentral/images/2022-05-18-22-29-40.png b/meshcentral/images/2022-05-18-22-29-40.png new file mode 100644 index 00000000..3a815fe8 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-29-40.png differ diff --git a/meshcentral/images/2022-05-18-22-29-47.png b/meshcentral/images/2022-05-18-22-29-47.png new file mode 100644 index 00000000..f8569fb7 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-29-47.png differ diff --git a/meshcentral/images/2022-05-18-22-32-35.png b/meshcentral/images/2022-05-18-22-32-35.png new file mode 100644 index 00000000..8253fbc2 Binary files /dev/null and b/meshcentral/images/2022-05-18-22-32-35.png differ diff --git a/meshcentral/images/2022-05-18-23-33-08.png b/meshcentral/images/2022-05-18-23-33-08.png new file mode 100644 index 00000000..bdfba9ab Binary files /dev/null and b/meshcentral/images/2022-05-18-23-33-08.png differ diff --git a/meshcentral/images/2022-05-18-23-34-22.png b/meshcentral/images/2022-05-18-23-34-22.png new file mode 100644 index 00000000..6bc35790 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-34-22.png differ diff --git a/meshcentral/images/2022-05-18-23-35-19.png b/meshcentral/images/2022-05-18-23-35-19.png new file mode 100644 index 00000000..c80d4a40 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-35-19.png differ diff --git a/meshcentral/images/2022-05-18-23-37-31.png b/meshcentral/images/2022-05-18-23-37-31.png new file mode 100644 index 00000000..0c4056a3 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-37-31.png differ diff --git a/meshcentral/images/2022-05-18-23-37-40.png b/meshcentral/images/2022-05-18-23-37-40.png new file mode 100644 index 00000000..ce071077 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-37-40.png differ diff --git a/meshcentral/images/2022-05-18-23-38-45.png b/meshcentral/images/2022-05-18-23-38-45.png new file mode 100644 index 00000000..d5797cb8 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-38-45.png differ diff --git a/meshcentral/images/2022-05-18-23-39-03.png b/meshcentral/images/2022-05-18-23-39-03.png new file mode 100644 index 00000000..dda6f11e Binary files /dev/null and b/meshcentral/images/2022-05-18-23-39-03.png differ diff --git a/meshcentral/images/2022-05-18-23-41-23.png b/meshcentral/images/2022-05-18-23-41-23.png new file mode 100644 index 00000000..368305cd Binary files /dev/null and b/meshcentral/images/2022-05-18-23-41-23.png differ diff --git a/meshcentral/images/2022-05-18-23-41-58.png b/meshcentral/images/2022-05-18-23-41-58.png new file mode 100644 index 00000000..94d6425b Binary files /dev/null and b/meshcentral/images/2022-05-18-23-41-58.png differ diff --git a/meshcentral/images/2022-05-18-23-42-51.png b/meshcentral/images/2022-05-18-23-42-51.png new file mode 100644 index 00000000..10ca5abe Binary files /dev/null and b/meshcentral/images/2022-05-18-23-42-51.png differ diff --git a/meshcentral/images/2022-05-18-23-46-26.png b/meshcentral/images/2022-05-18-23-46-26.png new file mode 100644 index 00000000..71646f29 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-46-26.png differ diff --git a/meshcentral/images/2022-05-18-23-49-37.png b/meshcentral/images/2022-05-18-23-49-37.png new file mode 100644 index 00000000..9d532149 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-49-37.png differ diff --git a/meshcentral/images/2022-05-18-23-49-53.png b/meshcentral/images/2022-05-18-23-49-53.png new file mode 100644 index 00000000..95e32c5f Binary files /dev/null and b/meshcentral/images/2022-05-18-23-49-53.png differ diff --git a/meshcentral/images/2022-05-18-23-53-04.png b/meshcentral/images/2022-05-18-23-53-04.png new file mode 100644 index 00000000..b45f268c Binary files /dev/null and b/meshcentral/images/2022-05-18-23-53-04.png differ diff --git a/meshcentral/images/2022-05-18-23-55-26.png b/meshcentral/images/2022-05-18-23-55-26.png new file mode 100644 index 00000000..d74b4837 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-55-26.png differ diff --git a/meshcentral/images/2022-05-18-23-56-29.png b/meshcentral/images/2022-05-18-23-56-29.png new file mode 100644 index 00000000..ea14f134 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-56-29.png differ diff --git a/meshcentral/images/2022-05-18-23-56-59.png b/meshcentral/images/2022-05-18-23-56-59.png new file mode 100644 index 00000000..07f126e2 Binary files /dev/null and b/meshcentral/images/2022-05-18-23-56-59.png differ diff --git a/meshcentral/images/2022-05-18-23-59-28.png b/meshcentral/images/2022-05-18-23-59-28.png new file mode 100644 index 00000000..f7d9529e Binary files /dev/null and b/meshcentral/images/2022-05-18-23-59-28.png differ diff --git a/meshcentral/images/2022-05-18_223720.png b/meshcentral/images/2022-05-18_223720.png new file mode 100644 index 00000000..b82aa68f Binary files /dev/null and b/meshcentral/images/2022-05-18_223720.png differ diff --git a/meshcentral/images/2022-05-19-00-00-05.png b/meshcentral/images/2022-05-19-00-00-05.png new file mode 100644 index 00000000..711ca1af Binary files /dev/null and b/meshcentral/images/2022-05-19-00-00-05.png differ diff --git a/meshcentral/images/2022-05-19-00-00-18.png b/meshcentral/images/2022-05-19-00-00-18.png new file mode 100644 index 00000000..acf07b1c Binary files /dev/null and b/meshcentral/images/2022-05-19-00-00-18.png differ diff --git a/meshcentral/images/2022-05-19-00-01-19.png b/meshcentral/images/2022-05-19-00-01-19.png new file mode 100644 index 00000000..7edd38c6 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-01-19.png differ diff --git a/meshcentral/images/2022-05-19-00-01-43.png b/meshcentral/images/2022-05-19-00-01-43.png new file mode 100644 index 00000000..3e7333d9 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-01-43.png differ diff --git a/meshcentral/images/2022-05-19-00-02-03.png b/meshcentral/images/2022-05-19-00-02-03.png new file mode 100644 index 00000000..2e60b3c2 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-02-03.png differ diff --git a/meshcentral/images/2022-05-19-00-03-32.png b/meshcentral/images/2022-05-19-00-03-32.png new file mode 100644 index 00000000..ac12616b Binary files /dev/null and b/meshcentral/images/2022-05-19-00-03-32.png differ diff --git a/meshcentral/images/2022-05-19-00-03-46.png b/meshcentral/images/2022-05-19-00-03-46.png new file mode 100644 index 00000000..e552b1fd Binary files /dev/null and b/meshcentral/images/2022-05-19-00-03-46.png differ diff --git a/meshcentral/images/2022-05-19-00-03-58.png b/meshcentral/images/2022-05-19-00-03-58.png new file mode 100644 index 00000000..eb6aaa09 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-03-58.png differ diff --git a/meshcentral/images/2022-05-19-00-19-29.png b/meshcentral/images/2022-05-19-00-19-29.png new file mode 100644 index 00000000..c33a8a5b Binary files /dev/null and b/meshcentral/images/2022-05-19-00-19-29.png differ diff --git a/meshcentral/images/2022-05-19-00-19-46.png b/meshcentral/images/2022-05-19-00-19-46.png new file mode 100644 index 00000000..8935509a Binary files /dev/null and b/meshcentral/images/2022-05-19-00-19-46.png differ diff --git a/meshcentral/images/2022-05-19-00-21-19.png b/meshcentral/images/2022-05-19-00-21-19.png new file mode 100644 index 00000000..50099f92 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-21-19.png differ diff --git a/meshcentral/images/2022-05-19-00-21-54.png b/meshcentral/images/2022-05-19-00-21-54.png new file mode 100644 index 00000000..8d1c6e1a Binary files /dev/null and b/meshcentral/images/2022-05-19-00-21-54.png differ diff --git a/meshcentral/images/2022-05-19-00-23-11.png b/meshcentral/images/2022-05-19-00-23-11.png new file mode 100644 index 00000000..9c30eb2b Binary files /dev/null and b/meshcentral/images/2022-05-19-00-23-11.png differ diff --git a/meshcentral/images/2022-05-19-00-25-11.png b/meshcentral/images/2022-05-19-00-25-11.png new file mode 100644 index 00000000..d691f7ee Binary files /dev/null and b/meshcentral/images/2022-05-19-00-25-11.png differ diff --git a/meshcentral/images/2022-05-19-00-32-32.png b/meshcentral/images/2022-05-19-00-32-32.png new file mode 100644 index 00000000..5553eb24 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-32-32.png differ diff --git a/meshcentral/images/2022-05-19-00-34-54.png b/meshcentral/images/2022-05-19-00-34-54.png new file mode 100644 index 00000000..94310f74 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-34-54.png differ diff --git a/meshcentral/images/2022-05-19-00-35-32.png b/meshcentral/images/2022-05-19-00-35-32.png new file mode 100644 index 00000000..4c0401b8 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-35-32.png differ diff --git a/meshcentral/images/2022-05-19-00-38-11.png b/meshcentral/images/2022-05-19-00-38-11.png new file mode 100644 index 00000000..7349b296 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-38-11.png differ diff --git a/meshcentral/images/2022-05-19-00-38-51.png b/meshcentral/images/2022-05-19-00-38-51.png new file mode 100644 index 00000000..8dfe3abf Binary files /dev/null and b/meshcentral/images/2022-05-19-00-38-51.png differ diff --git a/meshcentral/images/2022-05-19-00-39-35.png b/meshcentral/images/2022-05-19-00-39-35.png new file mode 100644 index 00000000..c6d5c047 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-39-35.png differ diff --git a/meshcentral/images/2022-05-19-00-39-42.png b/meshcentral/images/2022-05-19-00-39-42.png new file mode 100644 index 00000000..c865c0bd Binary files /dev/null and b/meshcentral/images/2022-05-19-00-39-42.png differ diff --git a/meshcentral/images/2022-05-19-00-40-00.png b/meshcentral/images/2022-05-19-00-40-00.png new file mode 100644 index 00000000..2fee5d8b Binary files /dev/null and b/meshcentral/images/2022-05-19-00-40-00.png differ diff --git a/meshcentral/images/2022-05-19-00-40-13.png b/meshcentral/images/2022-05-19-00-40-13.png new file mode 100644 index 00000000..1ba9fe5f Binary files /dev/null and b/meshcentral/images/2022-05-19-00-40-13.png differ diff --git a/meshcentral/images/2022-05-19-00-42-49.png b/meshcentral/images/2022-05-19-00-42-49.png new file mode 100644 index 00000000..94dc5878 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-42-49.png differ diff --git a/meshcentral/images/2022-05-19-00-44-03.png b/meshcentral/images/2022-05-19-00-44-03.png new file mode 100644 index 00000000..ddc3d4ba Binary files /dev/null and b/meshcentral/images/2022-05-19-00-44-03.png differ diff --git a/meshcentral/images/2022-05-19-00-44-25.png b/meshcentral/images/2022-05-19-00-44-25.png new file mode 100644 index 00000000..f92c1b06 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-44-25.png differ diff --git a/meshcentral/images/2022-05-19-00-45-31.png b/meshcentral/images/2022-05-19-00-45-31.png new file mode 100644 index 00000000..d86ad966 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-45-31.png differ diff --git a/meshcentral/images/2022-05-19-00-45-45.png b/meshcentral/images/2022-05-19-00-45-45.png new file mode 100644 index 00000000..46fb16bf Binary files /dev/null and b/meshcentral/images/2022-05-19-00-45-45.png differ diff --git a/meshcentral/images/2022-05-19-00-48-17.png b/meshcentral/images/2022-05-19-00-48-17.png new file mode 100644 index 00000000..ede1b9be Binary files /dev/null and b/meshcentral/images/2022-05-19-00-48-17.png differ diff --git a/meshcentral/images/2022-05-19-00-48-41.png b/meshcentral/images/2022-05-19-00-48-41.png new file mode 100644 index 00000000..d271a9c6 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-48-41.png differ diff --git a/meshcentral/images/2022-05-19-00-48-54.png b/meshcentral/images/2022-05-19-00-48-54.png new file mode 100644 index 00000000..3f091cc8 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-48-54.png differ diff --git a/meshcentral/images/2022-05-19-00-49-25.png b/meshcentral/images/2022-05-19-00-49-25.png new file mode 100644 index 00000000..f613b6b7 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-49-25.png differ diff --git a/meshcentral/images/2022-05-19-00-50-18.png b/meshcentral/images/2022-05-19-00-50-18.png new file mode 100644 index 00000000..edaabac1 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-50-18.png differ diff --git a/meshcentral/images/2022-05-19-00-50-30.png b/meshcentral/images/2022-05-19-00-50-30.png new file mode 100644 index 00000000..15bb58c5 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-50-30.png differ diff --git a/meshcentral/images/2022-05-19-00-50-52.png b/meshcentral/images/2022-05-19-00-50-52.png new file mode 100644 index 00000000..0d8620c6 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-50-52.png differ diff --git a/meshcentral/images/2022-05-19-00-51-14.png b/meshcentral/images/2022-05-19-00-51-14.png new file mode 100644 index 00000000..ce547fbd Binary files /dev/null and b/meshcentral/images/2022-05-19-00-51-14.png differ diff --git a/meshcentral/images/2022-05-19-00-51-29.png b/meshcentral/images/2022-05-19-00-51-29.png new file mode 100644 index 00000000..03f73e67 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-51-29.png differ diff --git a/meshcentral/images/2022-05-19-00-52-05.png b/meshcentral/images/2022-05-19-00-52-05.png new file mode 100644 index 00000000..59821a7f Binary files /dev/null and b/meshcentral/images/2022-05-19-00-52-05.png differ diff --git a/meshcentral/images/2022-05-19-00-52-36.png b/meshcentral/images/2022-05-19-00-52-36.png new file mode 100644 index 00000000..24e50520 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-52-36.png differ diff --git a/meshcentral/images/2022-05-19-00-52-53.png b/meshcentral/images/2022-05-19-00-52-53.png new file mode 100644 index 00000000..816f5c86 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-52-53.png differ diff --git a/meshcentral/images/2022-05-19-00-53-38.png b/meshcentral/images/2022-05-19-00-53-38.png new file mode 100644 index 00000000..3e70e88e Binary files /dev/null and b/meshcentral/images/2022-05-19-00-53-38.png differ diff --git a/meshcentral/images/2022-05-19-00-54-31.png b/meshcentral/images/2022-05-19-00-54-31.png new file mode 100644 index 00000000..62bfd8a9 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-54-31.png differ diff --git a/meshcentral/images/2022-05-19-00-54-50.png b/meshcentral/images/2022-05-19-00-54-50.png new file mode 100644 index 00000000..e3af2b78 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-54-50.png differ diff --git a/meshcentral/images/2022-05-19-00-55-29.png b/meshcentral/images/2022-05-19-00-55-29.png new file mode 100644 index 00000000..2668740c Binary files /dev/null and b/meshcentral/images/2022-05-19-00-55-29.png differ diff --git a/meshcentral/images/2022-05-19-00-56-05.png b/meshcentral/images/2022-05-19-00-56-05.png new file mode 100644 index 00000000..3a9255c4 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-56-05.png differ diff --git a/meshcentral/images/2022-05-19-00-57-06.png b/meshcentral/images/2022-05-19-00-57-06.png new file mode 100644 index 00000000..908ed342 Binary files /dev/null and b/meshcentral/images/2022-05-19-00-57-06.png differ diff --git a/meshcentral/images/2022-05-19-00-57-28.png b/meshcentral/images/2022-05-19-00-57-28.png new file mode 100644 index 00000000..b73b0bdc Binary files /dev/null and b/meshcentral/images/2022-05-19-00-57-28.png differ diff --git a/meshcentral/images/2022-05-31-10-30-07.png b/meshcentral/images/2022-05-31-10-30-07.png new file mode 100644 index 00000000..611c69c2 Binary files /dev/null and b/meshcentral/images/2022-05-31-10-30-07.png differ diff --git a/meshcentral/images/2022-05-31-10-30-42.png b/meshcentral/images/2022-05-31-10-30-42.png new file mode 100644 index 00000000..35772acd Binary files /dev/null and b/meshcentral/images/2022-05-31-10-30-42.png differ diff --git a/meshcentral/images/2022-05-31-10-30-50.png b/meshcentral/images/2022-05-31-10-30-50.png new file mode 100644 index 00000000..f3356cd0 Binary files /dev/null and b/meshcentral/images/2022-05-31-10-30-50.png differ diff --git a/meshcentral/images/2022-05-31-10-31-00.png b/meshcentral/images/2022-05-31-10-31-00.png new file mode 100644 index 00000000..6c779eab Binary files /dev/null and b/meshcentral/images/2022-05-31-10-31-00.png differ diff --git a/meshcentral/images/2022-05-31-10-32-46.png b/meshcentral/images/2022-05-31-10-32-46.png new file mode 100644 index 00000000..fef70118 Binary files /dev/null and b/meshcentral/images/2022-05-31-10-32-46.png differ diff --git a/meshcentral/images/2022-06-17-15-56-14.png b/meshcentral/images/2022-06-17-15-56-14.png new file mode 100644 index 00000000..3ad780dc Binary files /dev/null and b/meshcentral/images/2022-06-17-15-56-14.png differ diff --git a/meshcentral/images/2022-06-17-15-56-55.png b/meshcentral/images/2022-06-17-15-56-55.png new file mode 100644 index 00000000..dd50e316 Binary files /dev/null and b/meshcentral/images/2022-06-17-15-56-55.png differ diff --git a/meshcentral/images/2022-06-17-15-57-03.png b/meshcentral/images/2022-06-17-15-57-03.png new file mode 100644 index 00000000..65111b19 Binary files /dev/null and b/meshcentral/images/2022-06-17-15-57-03.png differ diff --git a/meshcentral/images/2022-06-17-15-57-15.png b/meshcentral/images/2022-06-17-15-57-15.png new file mode 100644 index 00000000..60810b55 Binary files /dev/null and b/meshcentral/images/2022-06-17-15-57-15.png differ diff --git a/meshcentral/images/2022-06-17-15-57-30.png b/meshcentral/images/2022-06-17-15-57-30.png new file mode 100644 index 00000000..2a8337c8 Binary files /dev/null and b/meshcentral/images/2022-06-17-15-57-30.png differ diff --git a/meshcentral/images/2022-06-17-15-57-52.png b/meshcentral/images/2022-06-17-15-57-52.png new file mode 100644 index 00000000..11f97e89 Binary files /dev/null and b/meshcentral/images/2022-06-17-15-57-52.png differ diff --git a/meshcentral/images/2022-07-02-06-27-36.png b/meshcentral/images/2022-07-02-06-27-36.png new file mode 100644 index 00000000..de0ba8fc Binary files /dev/null and b/meshcentral/images/2022-07-02-06-27-36.png differ diff --git a/meshcentral/images/2022-08-24-06-42-40.png b/meshcentral/images/2022-08-24-06-42-40.png new file mode 100644 index 00000000..7edcf6a2 Binary files /dev/null and b/meshcentral/images/2022-08-24-06-42-40.png differ diff --git a/meshcentral/images/2022-09-06-16-38-57.png b/meshcentral/images/2022-09-06-16-38-57.png new file mode 100644 index 00000000..4befd73b Binary files /dev/null and b/meshcentral/images/2022-09-06-16-38-57.png differ diff --git a/meshcentral/images/2023-02-24vscodejsonediting.png b/meshcentral/images/2023-02-24vscodejsonediting.png new file mode 100644 index 00000000..114e575b Binary files /dev/null and b/meshcentral/images/2023-02-24vscodejsonediting.png differ diff --git a/meshcentral/images/2023-11-29-12-57-15.png b/meshcentral/images/2023-11-29-12-57-15.png new file mode 100644 index 00000000..67718be7 Binary files /dev/null and b/meshcentral/images/2023-11-29-12-57-15.png differ diff --git a/meshcentral/images/2023-11-29-12-58-05.png b/meshcentral/images/2023-11-29-12-58-05.png new file mode 100644 index 00000000..f86536c8 Binary files /dev/null and b/meshcentral/images/2023-11-29-12-58-05.png differ diff --git a/meshcentral/images/2023-11-29-12-58-36.png b/meshcentral/images/2023-11-29-12-58-36.png new file mode 100644 index 00000000..deb41c14 Binary files /dev/null and b/meshcentral/images/2023-11-29-12-58-36.png differ diff --git a/meshcentral/images/2023-11-29_140845 - mesh json1.png b/meshcentral/images/2023-11-29_140845 - mesh json1.png new file mode 100644 index 00000000..b000fe1b Binary files /dev/null and b/meshcentral/images/2023-11-29_140845 - mesh json1.png differ diff --git a/meshcentral/images/2023-11-29_140845 - mesh json2.png b/meshcentral/images/2023-11-29_140845 - mesh json2.png new file mode 100644 index 00000000..d530454b Binary files /dev/null and b/meshcentral/images/2023-11-29_140845 - mesh json2.png differ diff --git a/meshcentral/images/7daypowerstate.png b/meshcentral/images/7daypowerstate.png new file mode 100644 index 00000000..c732043e Binary files /dev/null and b/meshcentral/images/7daypowerstate.png differ diff --git a/meshcentral/images/In-production.png b/meshcentral/images/In-production.png new file mode 100644 index 00000000..c1fc2e64 Binary files /dev/null and b/meshcentral/images/In-production.png differ diff --git a/meshcentral/images/OAuth-Internal-External.png b/meshcentral/images/OAuth-Internal-External.png new file mode 100644 index 00000000..13b14c8a Binary files /dev/null and b/meshcentral/images/OAuth-Internal-External.png differ diff --git a/meshcentral/images/agentico.png b/meshcentral/images/agentico.png new file mode 100644 index 00000000..dccb2ab1 Binary files /dev/null and b/meshcentral/images/agentico.png differ diff --git a/meshcentral/images/amt_troubleshoot1.png b/meshcentral/images/amt_troubleshoot1.png new file mode 100644 index 00000000..6fb86d83 Binary files /dev/null and b/meshcentral/images/amt_troubleshoot1.png differ diff --git a/meshcentral/images/amt_troubleshoot2.png b/meshcentral/images/amt_troubleshoot2.png new file mode 100644 index 00000000..69f145fb Binary files /dev/null and b/meshcentral/images/amt_troubleshoot2.png differ diff --git a/meshcentral/images/amt_troubleshoot3.png b/meshcentral/images/amt_troubleshoot3.png new file mode 100644 index 00000000..ce2df23e Binary files /dev/null and b/meshcentral/images/amt_troubleshoot3.png differ diff --git a/meshcentral/images/assistant_agent_code.png b/meshcentral/images/assistant_agent_code.png new file mode 100644 index 00000000..7ea019cd Binary files /dev/null and b/meshcentral/images/assistant_agent_code.png differ diff --git a/meshcentral/images/assistant_invitation_link.png b/meshcentral/images/assistant_invitation_link.png new file mode 100644 index 00000000..0b42376d Binary files /dev/null and b/meshcentral/images/assistant_invitation_link.png differ diff --git a/meshcentral/images/custom-web-icons.png b/meshcentral/images/custom-web-icons.png new file mode 100644 index 00000000..9edb5569 Binary files /dev/null and b/meshcentral/images/custom-web-icons.png differ diff --git a/meshcentral/images/desktop_buttonsbottomleft.png b/meshcentral/images/desktop_buttonsbottomleft.png new file mode 100644 index 00000000..e9367ae2 Binary files /dev/null and b/meshcentral/images/desktop_buttonsbottomleft.png differ diff --git a/meshcentral/images/desktop_buttonsbottomright.png b/meshcentral/images/desktop_buttonsbottomright.png new file mode 100644 index 00000000..e2d1b99d Binary files /dev/null and b/meshcentral/images/desktop_buttonsbottomright.png differ diff --git a/meshcentral/images/desktop_buttonstopright_actions.png b/meshcentral/images/desktop_buttonstopright_actions.png new file mode 100644 index 00000000..08c7a58a Binary files /dev/null and b/meshcentral/images/desktop_buttonstopright_actions.png differ diff --git a/meshcentral/images/desktop_buttonstopright_other.png b/meshcentral/images/desktop_buttonstopright_other.png new file mode 100644 index 00000000..b734aa76 Binary files /dev/null and b/meshcentral/images/desktop_buttonstopright_other.png differ diff --git a/meshcentral/images/desktop_connectbutton.png b/meshcentral/images/desktop_connectbutton.png new file mode 100644 index 00000000..4f97e8f0 Binary files /dev/null and b/meshcentral/images/desktop_connectbutton.png differ diff --git a/meshcentral/images/desktop_rdpconnectbutton.png b/meshcentral/images/desktop_rdpconnectbutton.png new file mode 100644 index 00000000..4c338622 Binary files /dev/null and b/meshcentral/images/desktop_rdpconnectbutton.png differ diff --git a/meshcentral/images/desktop_settings.png b/meshcentral/images/desktop_settings.png new file mode 100644 index 00000000..e03c6080 Binary files /dev/null and b/meshcentral/images/desktop_settings.png differ diff --git a/meshcentral/images/details_cpuram.png b/meshcentral/images/details_cpuram.png new file mode 100644 index 00000000..8898f52f Binary files /dev/null and b/meshcentral/images/details_cpuram.png differ diff --git a/meshcentral/images/determine-id.png b/meshcentral/images/determine-id.png new file mode 100644 index 00000000..4e1bf8b7 Binary files /dev/null and b/meshcentral/images/determine-id.png differ diff --git a/meshcentral/images/email-invitation.png b/meshcentral/images/email-invitation.png new file mode 100644 index 00000000..db9d2196 Binary files /dev/null and b/meshcentral/images/email-invitation.png differ diff --git a/meshcentral/images/faq_av_option1.png b/meshcentral/images/faq_av_option1.png new file mode 100644 index 00000000..718bae37 Binary files /dev/null and b/meshcentral/images/faq_av_option1.png differ diff --git a/meshcentral/images/gc-newproject.png b/meshcentral/images/gc-newproject.png new file mode 100644 index 00000000..715b68f9 Binary files /dev/null and b/meshcentral/images/gc-newproject.png differ diff --git a/meshcentral/images/gc-oauthconsent.png b/meshcentral/images/gc-oauthconsent.png new file mode 100644 index 00000000..a493c589 Binary files /dev/null and b/meshcentral/images/gc-oauthconsent.png differ diff --git a/meshcentral/images/gc-oauthconsent2.png b/meshcentral/images/gc-oauthconsent2.png new file mode 100644 index 00000000..10697dd1 Binary files /dev/null and b/meshcentral/images/gc-oauthconsent2.png differ diff --git a/meshcentral/images/gc-oauthcredentials.png b/meshcentral/images/gc-oauthcredentials.png new file mode 100644 index 00000000..243928b0 Binary files /dev/null and b/meshcentral/images/gc-oauthcredentials.png differ diff --git a/meshcentral/images/gc-oauthscopes.png b/meshcentral/images/gc-oauthscopes.png new file mode 100644 index 00000000..10697dd1 Binary files /dev/null and b/meshcentral/images/gc-oauthscopes.png differ diff --git a/meshcentral/images/gc-playground.webp b/meshcentral/images/gc-playground.webp new file mode 100644 index 00000000..a0fb99f6 Binary files /dev/null and b/meshcentral/images/gc-playground.webp differ diff --git a/meshcentral/images/general_alternateports.png b/meshcentral/images/general_alternateports.png new file mode 100644 index 00000000..ab3d0451 Binary files /dev/null and b/meshcentral/images/general_alternateports.png differ diff --git a/meshcentral/images/ldap_integration.png b/meshcentral/images/ldap_integration.png new file mode 100644 index 00000000..11f3994f Binary files /dev/null and b/meshcentral/images/ldap_integration.png differ diff --git a/meshcentral/images/rate_limiting_logins.png b/meshcentral/images/rate_limiting_logins.png new file mode 100644 index 00000000..26fe109d Binary files /dev/null and b/meshcentral/images/rate_limiting_logins.png differ diff --git a/meshcentral/images/terminal_connectbutton.png b/meshcentral/images/terminal_connectbutton.png new file mode 100644 index 00000000..52bbb888 Binary files /dev/null and b/meshcentral/images/terminal_connectbutton.png differ diff --git a/meshcentral/images/user_tokens1.png b/meshcentral/images/user_tokens1.png new file mode 100644 index 00000000..a81985f4 Binary files /dev/null and b/meshcentral/images/user_tokens1.png differ diff --git a/meshcentral/images/user_tokens2.png b/meshcentral/images/user_tokens2.png new file mode 100644 index 00000000..bcafaa1c Binary files /dev/null and b/meshcentral/images/user_tokens2.png differ diff --git a/meshcentral/index.html b/meshcentral/index.html new file mode 100644 index 00000000..99ec8043 --- /dev/null +++ b/meshcentral/index.html @@ -0,0 +1,4537 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MeshCentral2 Guide - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Meshcentral2 Guide

+

MeshCentral2 Guide

+

MeshCmd Guide as .pdf as .odt

+

Video Walkthru

+
+ +
+ +

Abstract

+

This user guide contains all essential information for the user to make full use of MeshCentral, a free open source web-based remote computer management software. The guide provides quick steps to setup administrative groups to remote control and manage computers in local network environments or via the Internet. Latter parts of the document will cover some advanced topics. The reader is expected to already have some of the basic understanding on computer networking, operating system and network security.

+

Introduction

+

MeshCentral is a free open source web-based remote computer management software. You could setup your own management server on a local network or on the internet and remote control and manage computers that runs either Windows* or Linux* OS.

+

+

To begin, a base or management server will be required. A management server could be any computing device (PC or VM) that has sufficient compute, storage and reliable network components to host an environment for MeshCentral and deliver good performance during remote management exercise. Whilst there are many configurations available for advanced users, typical server setup would only take just a few minutes to complete.

+

At a high level, there are only four (4) main steps: Setup, Install, Connect and Control.

+
    +
  1. Setup the MeshCentral server on VM or PC
  2. +
  3. Log on to MeshCentral portal with a valid account, creates an administrative mesh to collect all end-points (systems to be managed)
  4. +
  5. Generates an agent and installs it on a target or each end-point that immediately attempts a connection back to MeshCentral server.
  6. +
  7. Controls/manages assets or end-points that are available in respective administrative mesh
  8. +
+

Server Installation

+

Because the MeshCentral server is written in NodeJS it can be installed on many operating systems including Windows, Linux. Please refer to the MeshCentral Installer’s Guide available at https://www.meshcommander.com/meshcentral2 for information on how to install the server.

+

The server can be installed both on a local area network for local computer management and in the cloud for management of computers over the Internet. You can also install it on small IoT devices like a Raspberry Pi all the way to big servers. It’s recommended to get started with a test setup to get a feel for this server. Once installed, come back to this document for configuring and using your new server.

+

Basic Usage

+

In this section we will cover the basics of MeshCentral in your newly setup server.

+

Launch

+

Start your web browser and access MeshCentral via IP address/URL, http://serverFQDN/. If MeshCentral is running locally, enter http://127.0.0.1/. MeshCentral will redirect the browser to HTTPS if the server was accessed with HTTP. Once on HTTPS you will likely see this message: +

+

This is because by default MeshCentral is using a self-signed certificate that is not known to the browser as a “trusted” or “trustworthy” certificate. To prevent this warning from recurring, the following chapter will provide useful steps that can be considered.

+

To proceed on Firefox browser,

+
    +
  • Click on “Advanced”, “Add Exception” and “Confirm Security Exception”
  • +
+

To proceed on Chrome Browser,

+ +

Note: You can also get to a device by specifying the device name in the URL by adding ?viewmode=10&gotodevicename=MyComputer to the URL of the MeshCentral web page. The new gotodevicename will find a device with the specified name and navigate to that device’s page. This is perfect for integrating MeshCentral with other solutions but be aware that a computer name is not a unique identifier and so, &gotonode= is always the preferred way to access a device. This feature also works on the mobile web site.

+

Create Account

+

Create an account by clicking “Create One” and click “Create Account” once the text fields had been populated correctly.

+

+

New device group

+

Once logged in, create a new device group. This is a group of computers that we want to manage. To proceed,

+
    +
  1. Click on “Click here to create a new group of devices”,
  2. +
  3. Key in a suitable “Name”, .e.g. “SampleGroup”
  4. +
  5. Leave “Type” to default “Manage using a software agent” and click ‘OK”.
  6. +
+

+
+

Note

+

There are two types of groups:
+Software Agent Group: Commonly used to manage computers. Administrator must install a “remote management agent” on the remote computers.
+Intel® AMT Agent-less Group: Exclusive for remote computers that has Intel® AMT activated and needs to be managed independent of a “remote management agent”.

+
+

Add device

+

To add devices into new mesh

+
    +
  1. Click “Add Agent”,
  2. +
  3. Select the right Operating Systems (Windows* OS) and download the Mesh Agent executable.
  4. +
  5. Copy the Mesh Agent file into remote computers with Windows* OS +
  6. +
  7. Run Mesh Agent and Click “install” +
  8. +
+
+

Note

+

Mesh Agent is available for Windows* and Linux*. For Windows*, the mesh agent doesn’t contain any sensitive data and can copied and reused on many Windows* computers. For Linux*, instead of an executable, an installation script is provided to add remote computers. The script checks the type of computer and installs the proper agent automatically.

+
+

After agent install

+

Once the agents are installed, it will take up to a minute before the computer shows up on the user’s account automatically. Click on each computer to access it and user can rename the each computer with a unique name and icons.

+

+

+

Manage Computer

+

Click on any computer and go into the “Desktop” and “Files” tabs to remotely manage the computer or perform file transfer.

+

+

+

For advance users with console/command line interface experience, go into “Terminal” to perform scripting or quick tasks with CLI tools.

+

Desktop Control

+
+ +
+ +

Depending on how the agent is connected to the server, there are multiple methods to remote control. Mesh Agent, RDP, and AMT

+

For RDP connections, if you have previously saved the credentials that is usable by all users on the system. If you want to remove those saved credentials that's under the General Tab > Credentials. Click pen to clear them.

+

Server Certificate

+

As seen in the previous chapter, MeshCentral is setup with a self-signed certificate by default and the web browser will issue a warning concerning the validity of the certificate.

+

Users have few ways to handle this certificate warning:

+
    +
  • Ignore the warning and proceed with an exception in a recurring fashion. However, traffic from the server to the web browser remains encrypted. User must check the validity of the certificate presented by the website and compare with “webserver-cert-public.crt” file in the “meshcentral-data” folder of the server.
  • +
  • Add webserver’s root certificate into web browser’s trust list. Click on “Root Certificate” link at the bottom right of login page to download the root certificate of the web server and then add/import this as a trusted certificate into web browser. Some web browser may require a restart before the certificate installation takes effect.
  • +
  • If you own a domain name that points to your MeshCentral server, you can get a free trusted certificate using Let’s Encrypt (https://letsencrypt.org/). See the section on Let’s Encrypt in this document for more information on this option. MeshCentral has built-in support for Let’ Encrypt.
  • +
+
+

Important

+

Before adding/importing the certificate, user must check the validity of the certificate presented by the website and compare with “root-cert-public.crt” file in the “meshcentral-data” folder of the server.

+
+

For large scale deployments or setup, a legitimate trusted certificate is highly recommended for your web server. This way, any web browser that navigates to this web server will be able to readily verify its authenticity.

+
    +
  • If a legitimate trusted certificate is available, replace “webserver-cert-public.crt” and “webserver-cert-private.key” with your certificate. These files are located in “meshcentral-data” folder of the server.
  • +
  • If intermediate certificates are needed, add the files “webserver-cert-chain1.crt”, “webserver-cert-chain2.crt”, “webserver-cert-chain3.crt” respectively with the intermediate certificates.
  • +
+

Note: If you are using TLS offloading, see the section on “TLS Offloading” cover in the latter parts of this document.

+

Files and Folder Structure

+

It’s important to know the basic file and folder structure from which MeshCentral was installed as shown below

+

+

Right after running the “npm install meshcentral” command, the node_module folder will be created which contains meshcentral and all of its dependent modules. When the server executes for the first time, both meshcentral-data and meshcentral-files folders will be created.

+
+

Important

+

User must periodically backup both meshcentral-data and meshcentral-files which contains all of server’s data.

+
+

The “meshcentral-data” folder will contain:

+

meshcentral.db file: The server’s database file which contains all of the user and computer information. This includes account information and other sensitive information.

+

Five .key and .crt files: These are the server’s certificates and private keys. They are used to securely identify the server. The .key files must not be obtained by anyone else since they could be used to impersonate the server.

+

config.json file: This is the server’s configuration file. It first starts with a sample configuration that you can change. In a following section, we will discuss how to edit this file to customize the server.

+

The “meshcentral-files” folder contains user files that have been uploaded to the server. This folder can be quite large, especially if no user space quota is set in the config.json file. Users can upload a significant amount of files on the server.

+
+

Important

+

Back-up the “meshcentral-data” folder since this is the folder needed to reconstruct the server if something goes wrong. Without it, user will to start over. Recommended to apply suitable encryption on both folders given that they contain sensitive data.

+
+

Server Configuration File

+

In the “meshcentral-data” folder, there is a file called config.json that contains the main configuration of the server. A sample configuration file could look like this:

+
    {
+        "settings": {
+            "cert": "mesh.myserver.com",
+            "port": 8080,
+            "redirport": 81
+        },
+        "domains": {
+            "": {
+                "title": "MyServer",
+                "title2": "Servername",
+                "userQuota": 1048576,
+                "meshQuota": 248576,
+                "newAccounts" : 1
+            },
+            "Customer1": {
+                "title": "Customer1",
+                "title2": "Extra String",
+                "newAccounts" : 0
+            }
+        },
+        "peers": {
+            "serverId" : "Server1",
+            "servers": {
+                "Server1": { "url": "wss://192.168.1.100:443/" },
+                "Server2": { "url": "wss://192.168.1.101:443/" }
+            }
+        }
+    }
+
+

First, we will look at each of the top levels of the configuration file. The tops levels are “settings”, “domains”, “peers”, and “smtp” as shown in the table below.

+

+

Settings

+

As indicated before, the settings section of the config.json is equivalent to passing arguments to the server at runtime. Below is a list of settings that are available for the user.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Settings OptionDescription
CertSets the DNS name of the server. If this name is not set, the server will run in "LAN mode". When set, the server"s web certificate will use this name and the server will instruct agents and browsers to connect to that DNS name. You must set a server DNS name to run in "WAN mode". MeshCentral will not configure your DNS server. The DNS name must be configured separately.
PortThis sets the main web port used by the MeshCentral server and it"s the same port that users and mesh agents will connect to. The default port is 443, but if the port is busy, the next available higher port is used (.e.g. 444)
AliasPortSets the main port that will be used by the server externally. By default is the same as "Port" above, but can be set to be different when next. See "Server port aliasing" section for more details.
RedirPortThis is the port for redirecting traffic in the web server. When the server is configured with HTTPS, users that uses HTTP will be redirected to HTTPS. Port 80 is the default port. So, redirection will happen from port 80 to port 443.
MpsPortPort for Intel" AMT Management Presence Server to receive Intel" AMT CIRA (Client Initiated Remote Access) connections. The default is port 4433. This port is disabled in LAN mode. If user don"t plan on using Intel" AMT for management, this port can be left as-is.
TLSOffloadBy default this option is set to "false". If set to "true", server will run both web port and the Intel AMT MPS port without TLS with the assumption that a TLS offloading is taking care of this task. For further details, see the "TLS Offloading" section. This option can also be set to the IP address of the reverse-proxy in order to indicate to MeshCental to only trust HTTP X-Forwarded headers coming from this IP address. See the "Reverse-Proxy Setup" section for an example.
SelfUpdateWhen set to "true" the server will check for a new version and attempt to self-update automatically a bit after midnight local time every day. If set to a specific version such as "1.1.21" the server will immediately update to the specified version on startup if it's not already at this version.
SessionKeyThis is the encryption key used to secure the user"s login session. It will encrypt the browser cookie. By default, this value is randomly generated each time the server starts. If many servers are used with a load balancer, all servers should use the same session key. In addition, one can set this key so that when the server restarts, users do not need to re-login to the server.
MinifyDefault value is 0, when set to 1 the server will serve "minified" web pages, that is, web pages that have all comments, white spaces and other unused characters removed. This reduces the data size of the web pages by about half and reduced the number requests made by the browser. The source code of the web page will not be easily readable, adding "&nominify=1" at the end of the URL will override this option.
UserSpecify a username that browsers will be automatically logged in as. Useful to skip the login page and password prompts. Used heavily during development of MeshCentral.
NoUsersBy default this option is "false" and if set to "true", server will only accept users from localhost (127.0.0.1) and will not have a login page. Instead, a single user is always logged in. This mode is useful if user opts to setup MeshCentral as a local tool instead of as a multi-user server
MpsCertSpecifies the official name of the Intel AMT MPS server. If not specified, this is the same as the official server name specified by "cert". This option is generally used with MPS aliasing, see the "Server port aliasing" section for more information.
MpsAliasPortSpecify an alias port for the MPS server. See the section on "Server port aliasing" for use of this option.
ExactPortsIf this option is set to "true", only the exact port will be used. By default, if a port is in use, the server will try to bind the next available higher port. This is true for the "port", "redirport" and "mpsport" settings.
LanonlyServer"s default mode if not set with "--cert" option. If this option is set to "true", Intel" AMT MPS will be disabled, server name and fixed IP option will be hidden. Mesh agents will search for the server using multicast on the network.
WanonlyA recommended option when running MeshCentral in the cloud. If set to "true", server will run as a cloud service and assumes LAN features are disabled. For this option to work, the server must have a fixed IP or DNS record using the "--cert"" option. In this mode, LAN discovery features are disabled.
AllowFramingBy default is set to "false". If set to "true", web pages will be served in a way that allows them to be placed within an iframe of another web page. This is useful when you wish to add MeshCentral features into another website.
AllowLoginTokenBy default is set to "false". If set to "true", the server allows login tokens to be used in the URL as a replacement for user login. This is useful along with "allowFraming" option to embed MeshCentral features into another website.
MongoDBUsed to specify the MongoDB connection string. If not specified, MeshCentral will use the NeDB database with the file meshcentral.db in the meshcentral-data folder. To setup MongoDB, please refer to the Database section of this document.
MongoDBColUsed to specify the MongoDB collection name in the database. By default this value is "meshcentral". See Database section for more details on MongoDB setup.
DbEncryptKeySpecifies a password used to encrypt the database when NeDB is in use. If wanting to encrypt an existing database, use the "dbexport" and "dbimport" to save and reload the database with the encryption password set.
WebRTCSet to "true" or "false" depending if you want to allow the server to setup WebRTC communication. If WebRTC is setup, management traffic will flow directly between the browser and mesh agent, bypassing the server completely. The default is false now, but will be switched to true when WebRTC is ready for production.
ClickOnceSet to "true" or "false" to allow or disallow browser ClickOnce features. When enabled, browsers running on Windows will be shown extra options to allow RDP and other sessions thru the MeshCentral server. This requires ClickOnce browser support that is built-in to IE and available as add-in to Chrome and Firefox. Default is true.
+
+

Important

+

Changes in config.json will NOT take effect until server is restarted.

+
+

Note: We recommend the user to use a non-production server to experiment the setting options above.

+

Domains

+

In the domains section, you can set options for the default domain ("") in addition to creating new domains to establish a multi-tenancy server. For standard configuration, the root domain and other domains will be accessible like this:

+

https://servername:8080/ <- default domain

+

https://servername:8080/customer1 <- customer1 domain

+

https://servername:8080/customer2 <- customer2 domain

+

When a user setup many domains, the server considers each domain separately and each domain has separate user accounts, administrators, etc. If a domain has no users, the first created account will be administrator for that domain. Each domain has sub-settings as follows:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sub SettingsDescription
Title & Title2This are the strings that will be displayed at the banner of the website. By default title is set to “MeshCentral” and title2 is set to a version number
UserQuotaThis is the maximum amount of data in kilobytes that can be placed in the “My Files” tab for a user account.
MeshQuotaThis is the maximum amount of data in kilobytes that can be placed in the “My Files” tab for a given mesh
NewAccountsIf set to zero (0)
UserAllowedIPAllows user to set a list of allowed IP addresses. See section on server IP filtering.
AuthThis mode is often used in corporate environments. When server is running on Windows and this value is set to “sspi”, domain control authentication to the website is performed. In this mode, no login screen is displayed and browser will authenticate using the user’s domain credentials.
DnsThe DNS record for this domain. If specified, the domain is accessed using a DNS record like “customer1.servername.com” instead of “servername/customer1”. This feature requires the DNS server to be configured to point this server with a valid DNS record.
CertUrlLoad the TLS certificate for this domain from this https url. For example “https://127.0.0.1:123”. This option is useful when used along with the “TlsOffload” option. When MeshCentral is not doing any TLS but has a reverse-proxy or TLS offload device doing this work in front of the server, you can use this to have MeshCentral load the certificate from the server in front of MeshCentral.

This is needed because when agents connect, they need to be told that the certificate they saw upon connecting is the correct one. Using this, MeshCentral will know what certificate the agents are expected to see.
PasswordRequirementsUsed to specify the minimum password requirements for user authentication to this domain. By default, no password requirements are enforced but the user will see a password strength indicator that is not backed by any verifiable data.

The value must be set to an object, for example:

{ ""min"": 8, ""max"": 128, ""upper"": 1, ""lower"": 1, ""numeric"": 1, ""nonalpha"": 1 }

This indicated that passwords must be at least 8 characters long and have at least one upper case, one lower case, one numeric and one non-alphanumeric character. You can also set the maximum length of the password, however MeshCentral has already a limit of 256 characters. Specifying anything above this will have no effect.

Note that password requirements for Intel® AMT are defined by Intel and so, Intel® AMT passwords will always be verified using a separate set of requirements.
+

Note: When the DNS value is set for a domain, user can’t access the domain using “servername/customer1” instead it must be accessed with the valid DNS record and the DNS server should be setup to have two or more DNS records pointing to the same IP address.

+

In this mode, the server will serve a different TLS certificate depending on what DNS record is used to access the server.

+

+

As shown in the example above, we have two names that point to the same IP address. Since the configuration specifies the “dns” value, the second domain is only shown when the right name is used. We use “meshcentral” and “devbox” for DNS names, but in practice the user will use fully qualified domain names (FQDN) like “meshcentral.com” or “devbox.meshcentral.com”.

+

Server Peering

+

MeshCentral supports server peering. User could setup up many servers to share the task of handling incoming connections from managed clients and consoles. For server peering to function, all servers must have access to the same database, use the same certificates, the same configuration (with the exception of the server name) and servers must be able to communicate with each other behind a load balancer.

+

+

Hence, the user is expected to have good understanding on networking, server administration and applications to accomplish this setup. This document will not get into the details of setting up a load-balancer.

+ +

+

The setup flow above guides the user to pull together server peering setup with Meshcentral. (2) Shared storage is compulsory to host user files and it must be accessible from all of the servers. If the server is expected for critical work, replicated shared storage should be considered.

+

When Meshcentral is ready for peering setup (5), replicate the “meshcentral-data” directory on each server and configure the “peers” section of the config.json file as shown below.

+
    {
+        "peers": {
+            "serverId" : "Server1",
+            "servers": {
+                "Server1": { "url": "wss://192.168.1.100:443/" },
+                "Server2": { "url": "wss://192.168.1.101:443/" }
+            }
+        }
+    }
+
+

The configuration above assumes that server1 has an IP address of ‘192.168.1.100’ and server2 has ‘192.168.1.101’ respectively. The "serverId" value is a short and unique identifier for each server and it is optional. If it's not specified, the computer hostname is used instead.

+

The “servers” section of the configuration file should have the identifier of the server followed by each websocket URL and port (generally 443) of the peer servers. If the servers are running with “--tlsoffload”, then use “ws://” for the URL instead of “wss://”.

+

When the MongoDB is setup for the first time, a unique identifier is generated and written into the DB. To prevent situations where two servers with different database from peering together, during peering process, each server will validate among each other if they have the same unique DB identifier. Peering connection will only succeed if this condition is met.

+

Once peered, all of the servers should act like one single host, no matter which server the user(s) are connected to.

+

Email Setup

+

We highly recommend the use of an email server (SMTP) because we could allow MeshCentral to verify user account’s email address by sending a confirmation request to the user to complete the account registration and for password recovery, should a user forget account password as illustrated below

+

A verification email is sent when a new account is created or if the user requests it in the “My Account” tab.

+

+

The password recovery flow when “Reset Account” is triggered at the login page.

+

+

Both account verification and password recovery are triggered automatically once SMTP mail server configuration is included into the config.json file.

+

SMTP: User/Pass

+
Normal Server
+

Update the config.json with “smtp” section as shown below and restart the server.

+
{
+  "smtp": {
+    "host": "smtp.server.com",
+    "port": 25,
+    "from": "myaddress@server.com",
+    "user": "myaddress@server.com",      # Optional
+    "pass": "mypassword",                # Optional
+    "tls": false                         # Optional, default false
+  }
+}
+
+

Please map the host, port values to connect to the right host that provides this SMTP service. For “from” value, administrators may put something like donotreply@server.com, but often times it needs to be a valid address since SMTP server will not send out messages with an invalid reply address.

+

Some SMTP servers will require a valid username and password to login to the mail server. This is to prevent unauthorized e-mail correspondence. TLS option can be set to ‘true’ if the SMTP server requires TLS.

+
Gmail
+

One option is to configure MeshCentral work with Google Gmail by setting “host” with smtp.gmail.com, and “port” with 587. In the config.json file, use user’s Gmail address for both “from” and “user” and Gmail password in the “pass” value. You will also need to enable “Less secure app access” in for this Google account. It’s in the account settings, security section:

+

+

If a Google account is setup with 2-factor authentication, the option to allow less secure applications not be available. Because the Google account password is in the MeshCentral config.json file and that strong authentication can’t be used, it’s preferable to use a dedicated Google account for MeshCentral email.

+

SMTP: OAuth Authentication

+
Gmail
+

Google has announced that less secure app access will be phased out. For Google Workspace or G-Suite accounts, the following process can be used to allow OAuth2 based authentication with Google's SMTP server. It is likely a very similar process for regular Gmail accounts.

+

Start by visiting the Google API console:

+

https://console.developers.google.com/

+

First, you will create a new project. Name it something unique in case you need to create more in the future. In this example, I've named the project "MeshCentral"

+

+

Click on the "OAuth Consent Screen" link, Under "APIs and Services" from the left hand menu:

+

+

If you have a Google Workspace account, you will have the option to choose "Internal" application and skip the next steps. If not, you will be required to provide Google with information about why you want access, as well as verifying domain ownership.

+

+

Add the Gmail address under which you have created this project to the fields labelled ‘User support email’ and ‘Developer contact information’ so that you will be allowed for authentication. After that, you will want to add a scope for your app, so that your token is valid for gmail:

+

+

Once this is complete, the next step will be to add credentials.

+

+

Choose OAuth Client

+

You will obtain a Client ID and a Client secret once you've completed the process. Be sure to store the secret immediately, as you won't be able to retreive it after you've dismissed the window.

+

Next, you will need to visit the Google OAuth Playground:

+

https://developers.google.com/oauthplayground

+

+

Enter your Client ID and secret from the last step. On the left side of the page, you should now see a text box that allows you to add your own scopes. Enter https://mail.google.com and click Authorize API.

+

You will need to follow the instructions provided to finish the authorization process. Once that is complete, you should receive a refresh token. The refresh token, Client ID and Client Secret are the final items we need to complete the SMTP section of our config.json. It should now look something like this:

+
"smtp": {
+    "host": "smtp.gmail.com",
+    "port": 587,
+    "from": "my@googleaccount.com",
+    "auth": {
+      "clientId": "<YOUR-CLIENT-ID>",
+      "clientSecret": "<YOUR-CLIENT-SECRET>",
+      "refreshToken": "<YOUR-REFRESH-TOKEN>"
+    },
+    "user": "noreply@authorizedgooglealias.com",
+    "emailDelaySeconds": 10,
+    "tls": false,
+    "verifyEmail": true
+  }
+
+

Regardless of what SMTP account is used, MeshCentral will perform a test connection to make sure the server if working as expected when starting. Hence, the user will be notified if Meshcentral and SMTP server has been configured correctly as shown below.

+

+

After successfully configuring the Gmail SMTP server, switch the OAuth 'Publishing Status' from Testing to In Production. This step prevents the need for frequent refresh token generation. Verification of your project isn't required to make this change.

+

+

Database

+

A critical component of MeshCentral is the database. The database stores all of the user account information, groups and node data, historical power and event, etc. By default MeshCentral uses NeDB (https://github.com/louischatriot/nedb) that is written entirely in NodeJS and is setup automatically when MeshCentral is installed with the npm tool. The file “meshcentral.db” will be created in the “meshcentral-data” folder when MeshCentral is first launched. This database works well for small deployments scenarios.

+

Besides NeDB, MeshCentral fully supports MongoDB for larger deployments or deployments that require robust reliability or load-balancing. In this section we will see look at how to export and import the database file with a JSON file and how to configure MongoDB.

+

Database Export

+

User could use a practical approach to migrate from NeDB to MongoDB, by exporting the entire content of the existing NeDB into JSON file, setup the new MongoDB and import that JSON file to create the schemas in MongoDB.

+

To export the database, stop the MeshCentral server and run the server again with “--dbexport” and a JSON file called “meshcentral.db.json” will be created in the “meshcentral-data” folder as shown below.

+

+

Alternatively, user can also specify the full export path for the JSON file as shown below.

+

+

Database Import

+

Importing the MeshCentral database is useful when transitioning between database softwares (NeDB to/from MongoDB) or when importing the database from MeshCentral1 via migration tool.

+
+

Important

+

Importing a JSON file will overwrite the entire content of the database. A starting empty database is recommended.

+
+

When you are ready to import a JSON file into the database, run meshcentral with “--dbimport" as shown below. If path is not specified, the application will default to use “meshcentral.db.json” that is in “meshcentral-data” folder.

+

+

Alternatively, user can specify the full path of the import JSON as shown below.

+

+

Viewing the Database

+

For debugging purposes, Meshcentral allow users to have quick preview of certain frequently accessed data in the database with the following options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDescription
--showusersList of all users in the database.
--showmeshesList of all meshes in the database.
--shownodesList of all nodes in the database
--showeventsList all events in the database
--showpowerList all power events in the database.
--showallList all records in the database.
+

For example, you can show the list of users with the “--showusers"

+

+

MongoDB Setup

+

MongoDB is useful when setting up MeshCentral for two or more peer servers given that all peer servers much have access to the same database. NeDB and MongoDB have similar access interfaces hence the DB migration from one to the other is straight forward. Installing MongoDB depends on its host OS so do check for available download options at mongodb.com. +In this guide, we will focus on the 64-bit windows with SSL support installer.

+

+

After completing the installation step,

+
    +
  1. Stop any instance of Meshcentral that is running locally or in any machine
  2. +
  3. Start a terminal or Windows Command prompt (CMD),
  4. +
  5. Create a folder “c:\data\db”
  6. +
  7. +

    Go to the MongoDB bin folder and run “mongod --bind 127.0.0.1”.

    +

    This execute the database engine and store the database data in the default location “/data/db” path and bind a loopback on the local port “127.0.0.1”.

    +

    Note: Refer to MongoDB documentation to allow database to run in the background or experiment with alternate configurations.

    +

    +

    Note: Upon successful execution, MongoDB will wait for connections on its default port 27017.

    +
  8. +
  9. +

    Now run MeshCentral with the command below, it will tell Meshcentral to connect to MongoDB and use “meshcentral” DB. MongoDB will create this DB if it does not exist.

    +
    node meshcentral --mongodb mongodb://127.0.0.1:27017/meshcentral
    +
    +

    +
  10. +
  11. +

    Alternatively, to transition an existing meshcentral DB from NeDB and to MongoDB, just run the command below:

    +
    node meshcentral --dbexport 
    +node meshcentral --mongodb mongodb://127.0.0.1:27017/meshcentral --dbimport
    +node meshcentral --mongodb mongodb://127.0.0.1:27017/meshcentral
    +
    +
  12. +
  13. +

    We recommend the user to include MongoDB configuration into the server’s configuration “config.json” to avoid specifying the “--mongodb" each time MeshCentral is executed as shown below

    +
    {
    +  "settings": {
    +    "mongodb": "mongodb://127.0.0.1:27017/meshcentral",
    +    "mongodbcol": "meshcentral"
    +  }
    +}
    +
    +
  14. +
+

Note: By default, MeshCentral will create a single collections called “meshcentral” in the specified database. If user want to specify a different collection name, use “--mongodbcol" or “mongodbcol” for settings like shown above.

+

If you are using MongoDB with authentication, you can change the URL a little to add the username and password, for example:

+
mongodb://username:password@127.0.0.1:27017/meshcentral
+
+

You can also provide extra connection parameters like this:

+
mongodb://username:password@127.0.0.1:27017/meshcentral?authMechanism=MONGODB-CR&authSource=db
+
+

Running State-less

+

By default, MeshCentral will read its configuration information from the “meshcentral-data” folder. The most important file in that folder being the “config.json” file, but the folder also contains certificates, branding images, terms of service and more.

+

+

After the configuration is read, MeshCentral will connect to its database and continue to start the server. For most user’s this is a perfectly acceptable way to setup the server. However, in some cases, it’s advantageous to setup the server “state-less”. That is, there is no local configuration files at all and everything is in the database. Two examples of this would be when running MeshCentral is a Docker container where we don’t want the container to have any state or for compliance with security specifications where the database is “encrypted at rest”. In this cases, we will load the configuration files into the database and MeshCentral will only be told how to connect to the database.

+

+

When loading configuration information into the database, MeshCentral requires that a configuration file password be used to encrypt the configuration files in the database. This provides an additional layer of security on top of any authentication and security already provided by the database, if such security has been setup.

+

To make this happen, we will be using the following command line options from MeshCentral:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
--configkey (key)Specifies the encryption password that will be used to read or write the configuration files to the database.
--dblistconfigfilesList the names and size of all configuration files in the database.
--dbshowconfigfile (filename)Show the content of a specified filename from the database. --configkey is required.
--dbdeleteconfigfilesDelete all configuration files from the database.
--dbpushconfigfiles '*' or (folder path)Push a set of configuration files into the database, removing any existing files in the process. When * is specified, the “meshcentral-data” folder up pushed into the database. --configkey is required.
--dbpullconfigfiles (folder path)Get all of the configuration files from the database and place them in the specified folder. Files in the target folder may be overwritten. --configkey is required.
--loadconfigfromdb (key)Runs MeshCentral server using the configuration files found in the database. The configkey may be specified with this command or --configkey can be used.
+

Once we have MeshCentral running as expected using the “meshcentral-data” folder, we can simply push that configuration into the database and run using the database alone like this:

+
node ./node_modules/meshcentral --dbpushconfigfiles '*' --configkey mypassword
+
+node ./node_modules/meshcentral --loadconfigfromdb mypassword --mongodb "mongodb://127.0.0.1:27017/meshcentral"
+
+

This first line will load many of the “meshcentral-data” files into the database. At this point, we can back up the “meshcentral-data” folder and remove it. Then run the second line to start the server. Here we use MongoDB, but if one uses NeDB, the “meshcentral.db” file in the “meshcentral-data” folder will still be needed.

+

Note that MeshCentral does not currently support placing a Let’s Encrypt certificate in the database. Generally, one would use a reverse proxy with Let’s Encrypt support and TLS offload in the reverse proxy and then run MeshCentral in state-less mode in a Docket container.

+

Commandline Options

+

In general, doing --option value is the same as adding "option": value in the settings section of the config.json.

+

Here are the most common options found by running meshcentral --help

+
Run as a background service
+   --install/uninstall               Install MeshCentral as a background service.
+   --start/stop/restart              Control MeshCentral background service.
+
+Run standalone, console application
+   --user [username]                 Always login as [username] if account exists.
+   --port [number]                   Web server port number.
+   --redirport [number]              Creates an additional HTTP server to redirect users to the HTTPS server.
+   --exactports                      Server must run with correct ports or exit.
+   --noagentupdate                   Server will not update mesh agent native binaries.
+   --nedbtodb                        Transfer all NeDB records into current database.
+   --listuserids                     Show a list of a user identifiers in the database.
+   --cert [name], (country), (org)   Create a web server certificate with [name] server name.
+                                     country and organization can optionally be set.
+
+Server recovery commands, use only when MeshCentral is offline.
+   --createaccount [userid]          Create a new user account.
+   --resetaccount [userid]           Unlock an account, disable 2FA and set a new account password.
+   --adminaccount [userid]           Promote account to site administrator.
+
+

TLS Offloading

+

A good way for MeshCentral to handle a high traffic is to setup a TLS offload device at front of the server that takes care of doing all the TLS negotiation and encryption so that the server could offload this. There are many vendors who offer TLS or SSL offload as a software module (Nginx* or Apache*) so please contact your network administrator for the best solution that suits your setup.

+

As shown in the picture below, TLS traffic will come from the Internet and security will be handled by a device ahead of the server and MeshCentral only has to deal with TCP connections.

+

+

To make this work, it is important the server is setup with “--tlsoffload”. This indicates the server that TLS is already being taken care of and MeshCentral does not have to deal with it. MeshCentral will continue to listen to port 80, 443 and 4433.

+

However, incoming port 443 (main web port) and 4433 (Intel® AMT MPS port) will not have TLS but MeshCentral will still put many HTTPS flags in its responses on port 443. By default, if a user accesses http://127.0.0.1:443 without TLS offloader setting, the browser is expected to display warnings. To make this work, TLS offloader device’s ports and functions should be configured correctly like below

+ + + + + + + + + + + + + + + + + + + + + +
PortFunction Description
80Directly forwards port 80 to MeshCentral port 80
443Handle TLS using a web certificate and forward to MeshCentral port 443
4433Handle TLS using MPS certificate and forward to MeshCentral port 4433
+

If possible, port 443 should be configured with a legitimate trusted certificate and the public part of the certificate named as “webserver-cert-public.crt” must be placed inside of “meshcentral-data” folder of the server. When the server is executed in tlsoffload mode, only the public part of the web certificate is used by the server.

+

For Intel® AMT MPS port 4433, the certificate files “mpsserver-cert-public.crt” and “mpsserver-cert-public.key” must be copied from the “meshcentral-data” folder and loaded into the TLS offload module.

+

Note: Please consult the TLS offloader user manual from the respective vendor to configure TLS offloading feature correctly.

+

Let’s Encrypt support

+

MeshCentral makes use of HTTPS to authenticate and encrypt management traffic over the network. By default, a self-signed certificate is used for the MeshCentral HTTPS server. That certificate is not trusted by browsers and so, you get a warning message when visiting the web site. You can solve this but obtaining a free trusted certificate from Let’s Encrypt (https://letsencrypt.org/). There are some limitations and so, it’s best to get familiar with this service before starting. You will also need a valid domain name that you own and that points to your MeshCentral server.

+

+

Before moving forward with this section, make sure your MeshCentral server is working correctly, has a domain name pointing to it and that the HTTP redirection server on port 80 is enabled and working. MeshCentral’s HTTP port 80 server will be used in the process to prove to Let’s Encrypt that we have control over the domain. At any point, you may try to use https://letsdebug.net/ to see if your domain is setup correctly and/or debug any issues. When ready, add the “letsencrypt” section to the config.json file like this:

+
{
+  "settings": {
+    "RedirPort": 80,
+  },
+  "letsencrypt": {
+    "email": "myemail@myserver.com",
+    "names": "domain1.com,domain2.com",
+    "rsaKeySize": 3072,
+    "production": false
+  },
+}
+
+

The only mandatory field is the email address, please enter a valid one.

+

The names section is a list of domain names the requested certificate will be valid for. This must be a list of DNS names that are already pointing to your server. It’s important to understand you are not requesting these DNS names, rather, Let’s Encrypt will makes requests to prove control over all of these domain name before issuing the certificate. All the domain names you enter must point to the server and HTTP port 80 must be reachable over the internet. If you don’t specify names, the default MeshCentral certificate name is used, that is the configured “--cert [name]”.

+

The RSA key size can only be 2048 or 3072, with the default being 3072. This is the number of bit used for the RSA key in the certificate. Bigger is more secure, but takes more time to compute.

+

Lastly the production key, by default this is false. When set to false, MeshCentral will query the Let’s Encrypt staging server for a certificate. It’s highly recommended to try this first and make sure everything works before getting a real certificate. Keep production to false, run thru the process at least once and make sure everything works. You will get a new certificate installed on the HTTPS server signed by a staging Let’s Encrypt certificate authority.

+

The Let’s Encrypt certificates and files will be created in the “meshcentral-data” folder. Make sure to keep regular backups of the “meshcentral-data” folder and all sub-folders.

+

+

Once you placed the “letsencrypt” section in config.json, restart the server. The request to the Let’s Encrypt server may take a few minutes to a few hours. It’s best to have your DNS server name pointing to your server for over a day before doing this. Once the new certificate is received, the server will automatically restart and browsing to HTTPS on your server will show the new certificate. Here is what it looks like on FireFox:

+

+

If you successfully setup a Let’s Encrypt certificate using the Let’s Encrypt staging server (“production”: false) and everything looks good, stop the server, remove the “letsencrypt” folder in “meshcentral-data”, change production to “true” and start the server again. You should get a real certificate in a few minutes to a few hours. MeshCentral will automatically renew the certificate a few days before it expires. The MeshCentral self-signed certificate will still be present in the “meshcentral-data” folder, this is normal and there is no need to manually copy the Let’s Encrypt certificate to the “meshcentral-data” folder. If something goes wrong with the Let’s Encrypt certificate, the server will fall back to using the self-signed one.

+
+

Note

+

Please be patient with Let’s Encrypt certificate requests and make sure you correctly get a staging certificate before setting production to true.

+
+

If Let’s Encrypt works for you, please consider donating to them as they provide a critical service to the Internet community.

+

Server IP filtering

+

For improved security, it’s good to limit access to MeshCentral with IP address. For example, we want to allow mesh agents and Intel AMT computers to connect from anywhere, but whitelist IP address for users that we allow to access MeshCentral.

+

MeshCentral provides IP filtering option in the config.json file for each domain. For an example, we can set IP address whitelist for the default domain like as shown below.

+
    {
+        "domains": {
+            "": {
+                "userallowedip" : "1.2.3.4,1.2.3.5",
+            }
+        }
+    }
+
+

IP addresses are separated by a comma. As a result, only users coming these IP addresses will be able to see the server’s login page as illustrated below. Other IP addresses will be blocked effectively.

+

+

Note: When IP address whitelist is effective, Mesh Agent connection from any IP address will be not affected.

+

You can also use files for IP lists

+
"userAllowedIp": "file:userallowedips.txt",
+"userBlockedIp": "file:userblockedips.txt",
+"agentAllowedIp": "file:agentallowedips.txt"
+
+

Place the file in the meshcentral-data folder.

+

All the lines that start with a number or : will be used, everything else is ignored. So, you can put comments anyway you like, but probably best to start then with a # or something to make it clear.

+
# My list of blocked IP's
+185.101.70.0/24
+185.46.85.0/24
+37.9.44.0/24
+37.9.45.0/24
+5.189.205.0/24
+5.189.206.0/24
+5.189.207.0/24
+5.62.153.0/24
+5.62.156.0/24
+5.62.158.0/24
+
+# One more list
+5.8.44.0/24
+5.8.45.0/24
+5.8.46.0/24
+79.110.28.0/24
+79.110.31.0/24
+91.204.14.0/24
+95.181.218.0/24
+95.85.81.0/24
+
+

Embedding MeshCentral

+

One interesting way to use MeshCentral is to embed its features into another web site. In other words, certain feature of MeshCentral can be selectively embedded into another website such as Remote Desktop or File Transfer.

+

This allows another site to take care of the user accounts and business processes while MeshCentral takes care of remote management. In the example below, a user logs into an existing web site and received a page with MeshCentral remote desktop embedded into it.

+

+

To make this work, a following key alignment is required:
+1. When a user requests the business website, the business web server must return the user a web page containing an iframe with a URL that points to the MeshCentral server. +2. The URL must contain both a login token and embedding options. The login token tells MeshCentral under what MeshCentral account this request should be made. +3. The login token replaces the login screen of MeshCentral. Then, the embedding options can be used to specify no page title, header and footer to be displayed. This way, the page given by MeshCentral will fit nicely into the iframe.

+

In this section we will review both the login token and embedding options mentioned above.

+

Login Token

+

With MeshCentral, it’s possible to login to the main web page without even seeing the login screen. Of course, you can do this by specifying “--nousers" or “--user admin” when you run the server, but these approach are not secure as it removes user authentication for those accessing the server.

+

With login tokens feature, a token can be generated to be used for a short time to login and skip the login page. This is perfect for embedding MeshCentral usages into other web site and probably for other applications.

+

To enable this feature, configure config.json file to allow login tokens.

+
{
+  "settings": {
+    "allowLoginToken": true,
+    "allowFraming": true
+  }
+}
+
+

Set both allowLoginToken and allowFraming to ‘true’ to use login tokens along with framing MeshCentral within another web page.

+

Next, create a token. Execute MeshCentral with the “--logintoken [userid]” switch and userid value with the example below:

+

+

The “userid” is actually a combination of three values - user, domain, and username in a single string “user/domain/username”. The example above is using a default domain which is empty hence, the userid will be just “user//admin” to request for login token. Domains are only used if the server in multi-tenancy mode as discussed in previous chapters.

+

The resulting hashed base64 encoded blob can be used as a login token for 1 hour. Simply add the “?login=” followed by the token value generated to the URL of the webserver. For an e.g. https://localhost/?login=23tY7@wNbPoPLDeXVMRmTKKrqVEJ3OkJ. The login page is expected to be skipped and automatically login the user admin. This is just a manual attempt to token based login.

+

Now, to have this work seamlessly with a different website, we should generate a login token key. A token key can be used to generate login tokens whenever needed for MeshCentral. Generate this key with “--loginTokenKey" switch as shown below

+

+

The generated masker key must be placed in a secure location within the business website.

+

+

As illustrated above, we see the business site using the token key to generate a login token and embed it into the response web page. The user’s browser then loads the iframe that includes both the URL with the login token for MeshCentral. MeshCentral can then verify the token and allow the web page to load as expected.

+

Embedding Options

+

There are multiple options available for user to explicitly choose the features that will be loaded from MeshCentral to the business website. The argument in the in the URL can dictate which web page should display and how. The three embedding URL arguments are Viewmode, Hide and Node.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Embedding Options / URL ArgumentDescription"Values

Note: For values 10 and above, a node identifier must be specified."
viewmode"Indicates the information to show.
This is an integer value, possible values are:"
"1 = Devices tab

2 = Account tab

3 = Events tab

4 = Users tab (Site admins only)

5 = Server files tab

10 = Device general information

11 = Device remote desktop

12 = Device terminal

14 = Device Intel AMT console.

15 = Device Mesh Agent console
hide"Indicates which portion of the web page to hide.
This is a bitmask integer hence it will need the sum of values.
For .e.g.: To hide all of the values, add 1+2+4+8 and use 15 as the value.
1 = Hide the page header

2 = Hide the page tab

4 = Hide the page footer

8 = Hide the page title

16 = Hide the left tool bar

32 = Hide back buttons
nodeOptional unless Viewmode is set to value of 10 or greater.
Indicates which node to show on the screen,

For example, if we want to embed the remote desktop
page for a given node and hide the header, tabs,
footer and page title, we could have this URL:
https://localhost/?node=UkSNlz7t...2Sve6Srl6FltDd&viewmode=11&hide=15"
Node or NodeID is a long base64 encoded SHA384 value
+

Note: Typically, the URL for the website is followed by “?” then a set of name=value pairs separated by “&”.

+

Based on the URL https://localhost/?node=UkSNlz7t...2Sve6Srl6FltDd&viewmode=11&hide=15 , the nodeID starts with “UkSNlz7t”. We shortened the value in this example, but it’s normally a long base64 encoded SHA384 value. The Viewmode set to 11 which is the remote desktop page and Hide set to 15 to hide everything. Hence the user may see as illustrated below.

+

+

Only the remote desktop viewer will be displayed embedded within an iframe.

+

Note: User must set “allowFraming” to true in the config.json of the server. This is in addition to the Node, Viewmode and Hide arguments, the login token must be specified to add complex features into another website.

+

Server port aliasing

+

In some cases, you may be setting up a server on a private network that uses non-standard ports, but use a router or firewall in front to perform port mapping. So, even if the server privately uses non-standard ports, the public ports are the standard ports 80 and 443. You have to tell MeshCentral to bind to private ports but pretend it’s using the other standard ports when communicating publicly. To make this work, MeshCentral supports port aliasing.

+

For example you can run:

+
    node meshcentral --redirport 2001 --port 2002 --aliasport 443
+
+

+

Here, the server binds the HTTP and HTTPS ports to 2001 and 2002, but the server will externally indicate to MeshAgents and browsers that they must connect to port 443.

+

In a different situation, you may want to setup a server so that both Mesh Agents and Intel AMT connect back to the server on port 443. This is useful because some corporation have firewalls that restrict outgoing connections to only port 80 and 443. By default, MeshCentral will be setup to have MeshAgents connection on port 443 and Intel AMT on port 4433.

+

In the following picture we have a usual server running with:

+
node meshcentral --cert Server1 --port 443 --mpsport 4433
+
+

+

We can setup the server so that MeshAgent and Intel AMT will connect on port 443 of two different IP address or names like this:

+
node meshcentral --cert Server1 --mpscert Server2
+--port 443 --mpsport 4433 --mpsaliasport 443
+
+

+

In the second example, the server on the right is running HTTPS on port 443 and MPS on port 4433 as usual, but the MPS is now presenting a certificate that has the name “Server2” on it. The server will also configure Intel AMT CIRA to connect to “Server2:443”.

+

A router or firewall that is located in front of the MeshCentral server needs to be configured correctly to forwarding:

+
Server1:443 -> 443 on MeshCentral
+Server2:443 -> 4433 on MeshCentral
+
+

The routing of IP and ports by the firewall shown on the picture must be configured separately from MeshCentral using separate software. Typically, routers or firewalls have the proper controls to configure this type of traffic routes.

+

Web relay using DNS names and multiple web relays

+

MeshCentral has a web relay feature that allows a user to access remote web sites thru the MeshCentral server without having to install MeshCentral Router. Web relay also allow you to use an alternate DNS name instead of a different web relay port which has a few advantages. You can also use multiple alternate DNS names which can be used at the same time to provide users with many HTTP/HTTPS relays.

+

Video Walkthrus

+
+ + +
+ +

Device Groups with Relay Agent

+

MeshCentral supports the local device group allowing devices that do not have an agent to be managed thru MeshCentral with regular SSH, SFTP, RDP, VNC protocols. Until now, the MeshCentral server had to be in LAN or Hybrid modes to support his device group and the managed devices had to be on the same network as the MeshCentral server. Starting with v1.0.11, users can create a local device group specifying a MeshAgent as a relay. This makes it possible to manage agent-less devices from anywhere on the Internet even if the server is in WAN mode. Simply install a single device with a MeshAgent on a network and create a local device group with that device as the relay.

+

+

+

To enable SSH support, add this line to the domain section of your config.json:

+
"ssh": true
+
+

Video Walkthru

+
+ +
+ +

Raritan and WebPowerSwitch with Relay

+

In addition to local device groups, the IP-KVM/Power switch device group was also improved to support a MeshAgent as a relay. This is big news for Raritan IP-KVM switch owners as you can now monitor your IP-KVM ports and access them remotely from the Internet. The same can be done with WebPowerSwitch allowing full out-of-band remote access to devices from anywhere in the world.

+

+

+

+

NGINX Reverse-Proxy Setup

+

Video Walkthru

+
+ +
+ +

Sometimes it’s useful to setup MeshCentral with a reverse-proxy in front of it. This is useful if you need to host many services on a single public IP address, if you want to offload TLS and perform extra web caching. In this section we will setup NGINX, a popular reverse-proxy, in front of MeshCentral. NGNIX is available at: https://www.nginx.com/

+

+

In this example, we will:

+
    +
  • MeshCentral on non-standard ports, but alias HTTPS to port 443.
  • +
  • NGINX will be using standard ports 80 and 443.
  • +
  • We will have NGINX perform all TLS authentication & encryption.
  • +
  • MeshCentral will read the NGINX web certificate so agents will perform correct server authentication.
  • +
  • NGINX will be setup with long timeouts, because agents have long standard web socket connections.
  • +
+
+

Note

+

With SELinux, NGINX reverse proxy requires 'setsebool -P httpd_can_network_relay 1' +Caution: httpd_can_network_relay only allows certain ports +Confirm you are using ports from this subset in MeshCentral +If you want to use a different port then you will need to add it to http_port_t

+
+

Let’s get started by configuring MeshCentral with the following values in config.json:

+
{
+  "settings": {
+    "Cert": "myservername.domain.com",
+    "Port": 4430,
+    "AliasPort": 443,
+    "RedirPort": 800,
+    "AgentPong": 300,
+    "TlsOffload": "127.0.0.1"
+  },
+  "domains": {
+    "": {
+      "certUrl": "https://127.0.0.1:443/"
+    }
+  }
+}
+
+

With this configuration, MeshCentral will be using port 4430 instead of port 443, but because “TlsOffload” is set, TLS will not be performed on port 4430. The server name is set to “myservername.domain.com”, so that is the name that MeshCentral will give to agents to connect to. Also, the alias port is set to 443. So agents will be told to connect to “myservername.domain.com:443”.

+

The “AgentPong” line instructs the server to send data to the agent each 300 seconds and the agent by default will send data to the server every 120 seconds. As long as NGINX timeouts are longer than this, connections should remain open.

+

When agents connect, they will see the NGINX TLS certificate on port 443. MeshCentral needs to know about the NGINX certificate so that it can tell the agents this is the correct certificate they should expect to see. So, “certUrl” is used to tell MeshCentral where to get the certificates that agents will see when connecting.

+

When NGINX forwards connections to MeshCentral, extra X-Forwarded headers will be added to each request. MeshCentral needs to know if these headers can be trusted or not. By setting “TlsOffload” to “127.0.0.1”, MeshCentral is told to trust these headers when requests come from “127.0.0.1”.

+

In this example, make sure to change “127.0.0.1” to the IP address of NGINX and “Cert” to the external DNS name of the NGINX server.

+

Next, we need to configure and launch NGINX. Here is an ngnix.conf to get started:

+
worker_processes 1;
+
+events {
+    worker_connections 1024;
+}
+
+http {
+    # HTTP server. In this example, we use a wildcard as server name.
+    server {
+        listen 80;
+        server_name _;
+
+        location / {
+            proxy_pass http://127.0.0.1:800/;
+            proxy_http_version 1.1;
+
+            # Inform MeshCentral about the real host, port and protocol
+            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;
+        }
+    }
+
+    # HTTPS server. In this example, we use a wildcard as server name.
+    server {
+        listen 443 ssl;
+        server_name _;
+
+        # MeshCentral uses long standing web socket connections, set longer timeouts.
+        proxy_send_timeout 330s;
+        proxy_read_timeout 330s;
+
+        # We can use the MeshCentral generated certificate & key
+        ssl_certificate webserver-cert-public.crt;
+        ssl_certificate_key webserver-cert-private.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;
+
+            # Allows websockets over HTTPS.
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection "upgrade";
+            proxy_set_header Host $host;
+
+            # Inform MeshCentral about the real host, port and protocol
+            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;
+        }
+    }
+}
+
+

As indicated in the comments of this NGINX configuration file, we set timeouts to be really long. We forward HTTP port 80 and HTTPS port 443 to the corresponding ports on MeshCentral. In this example, we happen to use the web certificates that where generated by MeshCentral, but any certificate is ok. We also add extra “X-Forward” headers, this tells MeshCentral information that would normally be hidden by NGINX, like the client’s IP address and more.

+

Now we are ready to start NGINX and MeshCentral. You should start NGINX first because MeshCentral will try to fetch the certificate from NGINX upon start. When starting MeshCentral, you should see something like this:

+
MeshCentral HTTP redirection web server running on port 800.
+Loaded RSA web certificate at https://127.0.0.1:443/, SHA384: d9de9e27a229b5355708a3672fb23237cc994a680b3570d242a91e36b4ae5bc96539e59746e2b71eef3dbdabbf2ae138.
+MeshCentral Intel(R) AMT server running on myservername.domain.com:4433.
+MeshCentral HTTP web server running on port 4430, alias port 443.
+
+

Notice on the second line, MeshCentral will have loaded the web certificate from NGNIX and display a matching hash. That is it, navigating to port 80 and 443 on NGINX should show the MeshCentral web page and agents should connect as expected.

+

CIRA Setup with NGINX

+

We can add on the section above and support reverse proxy for Intel® AMT Client Initiated more Access (CIRA) connecting that come to the server. Normally, CIRA connections come on port 4433 and use TLS.

+

+

Since CIRA is a binary protocol, care must be taken to configure NGINX to handle the data as a TCP stream instead of HTTP. At the very bottom of the nginx.conf file, we can add the following:

+
stream {
+    # Internal MPS servers, in this case we use one MeshCentral MPS server is on our own computer.
+    upstream mpsservers {
+        server 127.0.0.1:44330 max_fails=3 fail_timeout=30s;
+    }
+
+    # We can use the MeshCentral generated MPS certificate & key
+    ssl_certificate mpsserver-cert-public.crt;
+    ssl_certificate_key mpsserver-cert-private.key;
+    ssl_session_cache shared:MPSSSL:10m;
+    ssl_ciphers HIGH:!aNULL:!MD5;
+    ssl_prefer_server_ciphers on;
+
+    # MPS server.
+    server {
+        listen 4433 ssl;
+        proxy_pass mpsservers;
+        proxy_next_upstream on;
+    }
+}
+
+NGINX will listen on port 4433, decrypt the connection and forward it to 44330 on the loopback interface. We are going to be used the “mpsserver” certificate that was created by MeshCentral as the TLS server certificate for port 4433. Now, we just have to make a few changes to the MeshCentral config.json file.
+
+{
+  "settings": {
+    "Cert": "myservername.domain.com"
+    "Port": 4430,
+    "AliasPort": 443,
+    "RedirPort": 800,
+    "TlsOffload": "127.0.0.1"
+    "MpsPort": 44330,
+    "MpsAliasPort": 4433,
+    "MpsTlsOffload": true
+  },
+  "domains": {
+    "": {
+      "certUrl": "https://127.0.0.1:443/"
+    }
+  }
+}
+
+

In this new config.json, we added 3 lines. First, the MeshCentral Management Presence Server (MPS) is now on port 44330. However, the MpsAliasPort value indicates that externally, port 4433 will be used, so we need to configure Intel AMT to connect to port 4433. Lastly, we want to disable TLS support on port 44330 by setting “MpsTlsOffload” to true.

+

With this configuration, Intel AMT CIRA connections will come in and TLS will be handled by NGINX. With this setup, it’s not possible to configure Intel AMT CIRA to connect using mutual-TLS authentication, only username/password authentication is used.

+

Traefik Reverse-Proxy Setup

+

In this section, we will setup MeshCentral with Traefik, a popular reverse proxy software. This section will be much like the previous section setting up NGNIX but with a different software and configuration file. Traefik is open source and available at: https://traefik.io/

+

This section covers a really simple Traefik configuration. Traefik is capable of a lot more complex configurations.

+

+

In this example, we will:

+
    +
  • MeshCentral on non-standard ports, but alias HTTPS to port 443.
  • +
  • Traefik will be using standard ports 80 and 443.
  • +
  • We will have Traefik perform all TLS authentication & encryption.
  • +
  • MeshCentral will read the NGINX web certificate so agents will perform correct server authentication.
  • +
+

First we will start with the MeshCentral configuration, here is a minimal configuration that will work:

+
{
+  "settings": {
+    "Cert": "myservername.domain.com",
+    "Port": 4430,
+    "AliasPort": 443,
+    "RedirPort": 800,
+    "TlsOffload": "127.0.0.1"
+  },
+  "domains": {
+    "": {
+      "certUrl": "https://127.0.0.1:443/"
+    }
+  }
+}
+
+

Note the “agentConfig” line: Because Traefik does not support web socket connections that are not “masked”, we have to tell the Mesh Agents to mask web socket connections using this line. Once set, any new agent will be installed with the web socket masking turned on. Also note that we will be running MeshCentral on port HTTPS/4430 and HTTP/800. However, we also indicate to MeshCentral that HTTPS will really be on port 443 using the “AliasPort” line.

+

The “TlsOffload” line indicates that MeshCentral should not perform TLS on port 4430. And the “certUrl” line indicates what URL can be used to load the external certificate that will be presented on port 443 in front of MeshCentral.

+

Now that we have MeshCentral setup, let’s take a look at a sample Traefik configuration file. In this case, we will manually configure the entrypoints, frontends and backends within the Traefik configuration file. There is a basic configuration file for Traefik 1.7:

+
[global]
+  checkNewVersion = false
+  sendAnonymousUsage = false
+
+[entryPoints]
+  [entryPoints.http]
+  address = ":80"
+    [entryPoints.http.redirect]
+    entryPoint = "https"
+  [entryPoints.https]
+  address = ":443"
+    [entryPoints.https.tls]
+      [[entryPoints.https.tls.certificates]]
+      certFile = "webserver-cert-public.crt"
+      keyFile = "webserver-cert-private.key"
+
+[file]
+
+[backends]
+  [backends.backend1]
+    [backends.backend1.healthcheck]
+      path = "/health.ashx"
+      interval = "30s"
+
+    [backends.backend1.servers.server1]
+    url = "http://127.0.0.1:4430"
+    weight = 1
+
+[frontends]
+  [frontends.frontend1]
+  entryPoints = ["https"]
+  backend = "backend1"
+  passHostHeader = true
+  [frontends.frontend1.routes]
+    [frontends.frontend1.routes.main]
+    rule = "Host:myserver.domain.com,localhost"
+
+[api]
+  entryPoint = "traefik"
+  dashboard = true
+
+

The enterPoints section shows we have two entry points, port 80 will be redirected to port 443. Traefik will perform this redirection so MeshCentral will never see port 80 connections. Port 443 will be setup using the given TLS certificates. In this example, we just used the certificate files generated by MeshCentral in the “meshcentral-data” folder. You can use the two certificate files as-is.

+

The backends section configures one MeshCentral server on port “4430”. Traefik will additionally check the health of the MeshCentral server periodically, every 30 seconds.

+

The frontends section is what routes the connections coming in the entry points to the backend servers. In this case, the HTTPS entry point is routed to the MeshCentral server is the hostname matches “myserver.domain.com” or “localhost”.

+

Finally, the API section creates a web portal on port 8080 for monitoring of Traefik.

+

HAProxy Reverse-Proxy Setup

+

In this section, we will setup MeshCentral with HAProxy, a small popular reverse proxy software. This section will be much like the previous sections setting up NGNIX and Traefik but with a different software and configuration file. HAProxy is free and available at: https://www.haproxy.org/

+

+

This section covers a really simple configuration. HAProxy is capable of a lot more complex configurations. In the following example, HAProxy will perform TLS and forward the un-encrypted traffic to MeshCentral on port 444. HAProxy will add extra “X-Forwarded-Host” headers to the HTTP headers so that MeshCentral will know from the IP address the connection comes from.

+

+

In the following configuration file, we have browser connections on port 80 being redirected to HTTPS port 443. We also have Let’s Encrypt cert bot for getting a real TLS certificate and “mesh.sample.com” being redirected to 127.0.0.1:444.

+
global
+    log /dev/log local0
+    log /dev/log local1 notice
+    chroot /var/lib/haproxy
+    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
+    stats timeout 30s
+    user haproxy
+    group haproxy
+    daemon
+
+defaults
+    log global
+    mode http
+    option httplog
+    option dontlognull
+    option forwardfor
+    option http-server-close
+
+frontend http
+    bind *:80
+    redirect scheme https code 301 if !{ ssl_fc }
+
+frontend https
+    bind *:443 ssl crt /etc/haproxy/cert.pem
+    http-request add-header X-Forwarded-Proto https
+    acl acmepath path_beg /.well-known/acme-challenge/
+    acl meshcentralhost hdr(host) -i mesh.sample.com
+    acl meshcentralhost hdr(host) -i mesh.sample.com:443
+    use_backend acme if acmepath
+    use_backend meshcentral if meshcentralhost
+
+backend acme
+    server certbot localhost:54321
+
+backend meshcentral
+    http-request add-header X-Forwarded-Host %[req.hdr(Host)]
+    server meshcentral 127.0.0.1:444
+
+

On the MeshCentral side, we are not going to use port 80 and need the main HTTPS port to not perform TLS and listen on port 444.

+
{
+  "settings": {
+    "Cert": "myservername.domain.com",
+    "Port": 444,
+    "AliasPort": 443,
+    "RedirPort": 0,
+    "TlsOffload": "127.0.0.1"
+  },
+  "domains": {
+    "": {
+      "certUrl": "https://127.0.0.1:443/"
+    }
+  }
+}
+
+

We also specify “127.0.0.1” in TLS offload since we want MeshCentral to make use of the X-Forwarded-Host header that is set by HAProxy.

+

Running in a Production Environment

+

When running MeshCentral is a production environment, administrators should set NodeJS to run in production mode. There is a good article here (http://www.hacksparrow.com/running-express-js-in-production-mode.html) on what this mode is and how to set it. This mode will also boost the speed of the web site on small devices like the Raspberry Pi. To run in production mode, the environment variable “NODE_ENV” must be set to “production”. On Linux, this is done like this:

+
export NODE_ENV=production
+
+

On Windows, it’s done like this:

+
SET NODE_ENV=production
+
+

Special care must be taken to set the environment variable in such a way that if the server is rebooted, this value is still set. Once set, if you run MeshCentral manually, you will see:

+
MeshCentral HTTP redirection web server running on port 80.
+MeshCentral v0.2.2-u, Hybrid (LAN + WAN) mode, Production mode.
+MeshCentral Intel(R) AMT server running on devbox.mesh.meshcentral.com:4433.
+MeshCentral HTTPS web server running on devbox.mesh.meshcentral.com:443.
+
+

In production mode, ExpressJS will cache some files in memory making the web server much faster and any exceptions thrown by the ExpressJS will not result in the stack trace being sent to the browser.

+

Two step authentication

+

If the MeshCentral server is setup with a certificate name and not setup to use Windows domain authentication, then users will have the options to use 2-step authentication using the Google Authenticator application or any compatible application. Use of this option should be encouraged for users that manage a lot of critical computers. Once active the users will need to enter their username, password and a time limited token to login.

+

To get this features setup, users will need to go to the “My Account” tab or the “My Account” menu in the mobile application. They then select, “Add 2-stop login” and follow the instructions.

+

+

Note that if a user performs a password recovery using email, the 2-step authentication is then turned off and will need to be turned on again. This is not idea as someone being able to intercept the user’s email could still log into the web site. Users should make sure to properly protect their email account.

+

Another form of MFA or Multi-factor Authentication is hardware based OTP (One Time Password) solution providing 2FA or Two-factor authentication. Yubikey is fully supported in MeshCentral.

+
+ +
+ +

And taking authentication to the next step is removing the login page entirely. Use LoginKey 3FA with MeshCentral.

+
+ +
+ +

Server Backup & Restore

+

It’s very important that the server be backed up regularly and that a backup be kept offsite. Luckily, performing a full backup of the MeshCentral server is generally easy to do. For all installations make sure to back up the following two folders and all sub-folders.

+
meshcentral-data
+meshcentral-files
+
+

If using NeDB that is built into MeshCentral, you are done. If you are running MongoDB, you will need to perform an extra step. In the command shell, run mongodump to archive all of the MongoDB databases.

+
mongodump --archive=backup.archive
+
+

Then, keep the backup.archive file in a safe place. It’s critical that the content of meshcentral-data be backed up in a secure location and preferably using encryption, this is because it contains certificates that give this server its unique personality. Once agents are installed, they will only connect to this server and no other. If you reinstall MeshCentral, even if it is with the same domain name, agents will not connect to the new server since the server certificates are different. Also, someone with access to a backup of “meshcentral-data” could impersonate the server.

+

To restore back backup, just install a MeshCentral server, make sure it works correctly. Stop it, wipe the old “meshcentral-data” and “meshcentral-files” and put the backup version instead. If using MongoDB, copy the backup.archive back, make sure to clean up any existing “meshcentral” database, run “mongo” and type:

+
use meshcentral
+db.dropDatabase()
+
+

Then exit with Ctrl-C and run:

+
mongorestore --archive=backup.archive
+
+

This will re-import the database from the backup. You can then start MeshCentral again.

+
+

Note

+

The two values for backup and restore in the json are only valid for databases backed by NeDB

+
+

Backup to Google Drive

+
sudo systemctl stop meshcentral.service
+nano /opt/meshcentral/meshcentral-data/config.json
+
+

Remove underscored items

+

+
sudo systemctl start meshcentral.service
+sudo systemctl status meshcentral.service
+
+

Log into your MC:

+

+

+

Create desktop app

+

+

Enter the Client ID and Client Secret into MC

+

+

+

HashiCorp Vault support

+

MeshCentral has built-in support for HashiCorp Vault so that all configuration and certificates used by MeshCentral are retrieved from a Vault server. Vault is a secret store server and when used with MeshCentral, the MeshCentral server will not be storing any secrets locally. You can get started with Vault here: https://www.vaultproject.io/

+

Once you got a MeshCentral server working correctly, you can start a simple demonstration Vault server by typing:

+
vault server -dev
+
+

When you run the server in developer mode, you will see a secret token and unseal key on the screen. These two values will be used in the commands to follow. You can load the configuration file and all certificates from “meshcentral-data” into Vault by typing this:

+
node node_modules/meshcentral --vaultpushconfigfiles --vault http://127.0.0.1:8200 --token s.cO4… --unsealkey 7g4w… --name meshcentral
+
+

Once all of the files have been written into Vault, you can take a look at the Vault web user interface to see all of the secrets. It will be in “secret/meshcentral”:

+

+

The “config.json” and “terms.txt” files and files in “meshcentral-data” that end with “.key”, “.crt”, “.jpg” and “.png” will be stored in Vault. You can then run MeshCentral like this:

+
node node_modules/meshcentral --vault http://127.0.0.1:8200 --token s.cO4… --unsealkey 7g4w… --name meshcentral
+
+

MeshCentral will first read all of the files from Vault and get started. An alternative to this is to create a very small config.json file in “meshcentral-data” that contains only the Vault configuration like this:

+
{
+  "settings": {
+    "vault": {
+      "endpoint": "http://127.0.0.1:8200",
+      "token": "s.cO4Q…",
+      "unsealkey": "7g4wFC…",
+      "name": "meshcentral"
+    }
+  }
+}
+
+

Once the config.json file is setup, you can just run MeshCentral without any arguments.

+
node node_modules/meshcentral
+
+

Lastly you can all pull all of the files out of Vault using this command line:

+
node node_modules/meshcentral --vaultpullconfigfiles --vault http://127.0.0.1:8200 --token s.cO4… --unsealkey 7g4w… --name meshcentral
+
+

And delete the Vault secrets using this:

+
node node_modules/meshcentral --vaultdeleteconfigfiles --vault http://127.0.0.1:8200 --token s.cO4… --unsealkey 7g4w… --name meshcentral
+
+

Database Record Encryption

+

Regardless if using the default NeDB database or MongoDB, MeshCentral can optionally encrypt sensitive data that is stored in the database. When enabled, this encryption is applied to user credentials and Intel AMT credentials.

+

+

The additional encryption does the affect database operations and can be used in addition to additional database security. In the following image, we see on the left a normal user record including user credential hashes and data required for two-factor authentication. On the right side, these values are encrypted using AES-256-GCM in the “_CRYPT” field.

+

+

Only some data fields are encrypted and the “_CRYPT” entry will only be present when one or more fields are present that need to be secured. To enable this feature, add the “DbRecordsEncryptKey” with a password string to the “settings” section of the config.json like this:

+
{
+  "settings": {
+    "Port": 4430,
+    "RedirPort": 800,
+    "DbRecordsEncryptKey": "MyReallySecretPassword"
+  }
+}
+
+

The provided password will be hashed using SHA384 and the result with be used as an encryption key. When DbRecordsEncryptKey is set, any new or updated records that are written will be encrypted when needed. Existing encrypted records will be read and decrypted as needed. You can force the all entries to be re-written by running:

+
node node_modules/meshcentral --recordencryptionrecode
+
+

This command will re-write entries in the database that could require added security and force the application of record encryption. You can also specify a key for decryption only like this:

+
{
+  "settings": {
+    "Port": 4430,
+    "RedirPort": 800,
+    "DbRecordsDecryptKey": "MyReallySecretPassword"
+  }
+}
+
+

When set, the key will only be used for decryption and any new or updated records in the database will not be written with record encryption. You can then run this command again to force all records to be rewritten without encryption:

+
node node_modules/meshcentral --recordencryptionrecode
+
+

It’s really important to keep the encryption key in a safe place along with database backups. If the database is backed up but the record encryption key is lost, it will not be possible to recover the secured data in the database.

+

Also note that database record encryption can and should be used along with other data protection systems.

+

MongoDB free server monitoring

+

If running with MongoDB version 4.x, there is a free database monitoring service that is provided. Just run “mongo” and you may see the following:

+

+

Type “db.enableFreemonitoring()” if you want to enable this. You will be given a URL to access the data and can turn it back off at any time. The web page will look something like this:

+

+

In addition to database specific information, the graphs track CPU, memory and disk usage. This can be useful to track how well the server is responding under load.

+

MeshCentral Single Sign-On (SSO)

+

As with any web application deployed in organization, it’s convenient and more secure for users to have a single set of credentials that can be used across many services. In this section we take a look at how to configure MeshCentral so that you can sign-in using credentials from other services. This allows users to completely skip creating a user account on MeshCentral or having to remember usernames and password for one more web site. There are two single sign-on protocols that are supported in MeshCentral, OAuth2 and SAML. We will take a look at an example for each one.

+

Before you get started, your MeshCentral server must be publicly facing on the internet and have a valid TLS certificate. For example, by setting up Let’s Encrypt. After the web site is working correctly user the steps below.

+

LDAP

+
+ +
+ +

You can integrate LDAP using these configuration options

+

+

Twitter Authentication

+

Like many other services, Twitter allows its users to login to other web site using Twitter credentials using OAuth2. Start by creating an account on Twitter and logging in. Then navigate to https://developer.twitter.com/en/apps, this is where you can create new applications that are compatible with Twitter.

+

Start by creating a new application and fill in the application form. Give your application and name, description, server URL and more.

+

+

Make sure to select “Enable Sign in with Twitter” and set the callback URL to “https://(server.domain.com)/auth-twitter-callback”. This is the URL that Twitter will redirect users to once they are logged in. For example this is what a sample application would look like:

+

+

Once the new application is created, go to the “Keys and tokens” tab. You will need the “API Key” and “API secret key” values. In the MeshCentral config.json, place these two values as “clientid” and “clientsecret” of the Twitter section of the “AuthStrategies”.

+

+

Once done, your config.json should look a bit like this:

+
{
+  "settings": {
+    "Cert": "myserver.mydomain.com",
+    "Port": 443,
+    "RedirPort": 80
+  },
+  "domains": {
+    "": {
+      "Title": "MyServer",
+      "Title2": "Servername",
+      "NewAccounts": true,
+      "authStrategies": {
+        "twitter": {
+          "clientid": "xxxxxxxxxxxxxxxxxxxxxxx",
+          "clientsecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+        }
+      }
+    }
+  }
+}
+
+

Note that if you do not allow new accounts, any new users that use Twitter credentials will not be able to login to MeshCentral. One trick is to allow new account, login and change this setting again. Once the config.json is correct, restart the server and you should see the Twitter icon on the login screen. When restarting the MeshCentral server, new modules will need to be installed to support this new feature. Depending on how your server is setup, you may need to restart the server manually to allow the new modules to be installed.

+

+

Google, GitHub, Reddit Authentication

+

The exact same process as shown in the previous section can be repeated for Google, GitHub and Reddit. In each case, you need to go to each respective credential provider and get a “ClientID” and “ClientSecret” for each service. You also need to register the correct callback URL for each service. Take a look at the config.json below and note the callback URL that will need to be registered for each service provider.

+
{
+  "settings": {
+    "Cert": "myserver.mydomain.com",
+    "Port": 443,
+    "RedirPort": 80
+  },
+  "domains": {
+    "": {
+      "Title": "MyServer",
+      "Title2": "Servername",
+      "NewAccounts": true,
+      "authStrategies": {
+        "twitter": {
+          "__callbackurl": "https://server/auth-twitter-callback",
+          "clientid": "xxxxxxxxxxxxxxxxxxxxxxx",
+          "clientsecret": " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "
+        },
+        "google": {
+          "__callbackurl": "https://server/auth-google-callback",
+          "clientid": "xxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
+          "clientsecret": "xxxxxxxxxxxxxxxxxxxxxxx"
+        },
+        "github": {
+          "__callbackurl": "https://server/auth-github-callback",
+          "clientid": "xxxxxxxxxxxxxxxxxxxxxxx",
+          "clientsecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+        },
+        "reddit": {
+          "__callbackurl": "https://server/auth-reddit-callback",
+          "clientid": "xxxxxxxxxxxxxxxxxxxxxxx",
+          "clientsecret": " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "
+        }
+      }
+    }
+  }
+}
+
+

It’s possible to enable all four of these service providers at the same time to offer the most flexibility for users. Note that when using an OAuth service provider, MeshCentral does not offer two-factor authentication since it will be handled by the provider depending on user configuration.

+

Microsoft Azure Active Directory

+

In this section we look at how to setup MeshCentral to Azure Active Directory using OAuth. Like all other sections about setting up single sign-on, make sure your MeshCentral server is already setup on the public Internet with a valid TLS certificate. You can then start by adding a new application registration to the Azure portal.

+

+

+

We give our application a name, generally the domain name of the MeshCentral server is a good choice. Then you can setup the redirect URL to https://[servername]/auth-azure-callback. Make sure to type this correctly, all lower case with the full domain name of your MeshCentral server. Once done, there are two values we will need later, the Application ID and Tenant ID.

+

+

Next, we need to create a secret that will be shared between Azure and MeshCentral. Go to the “Certificates & secrets” section and click “New client secret”. You then enter a name and for our example, we will opt to never make it expire.

+

+

+

We then copy the resulting secret and this will be the 3rd and final value we need to get MeshCentral setup. Now, we take the application ID, tenant ID and secret and place these values in the MeshCentral config.json like so:

+
{
+  "settings": {
+    "Cert": "myserver.mydomain.com",
+    "Port": 443,
+    "RedirPort": 80
+  },
+  "domains": {
+    "": {
+      "Title": "MyServer",
+      "Title2": "Servername",
+      "NewAccounts": false,
+      "authStrategies": {
+        "azure": {
+          "newAccounts": true,
+          "clientid": "be4aadd3-77b8-4e55-af8a-4b8e2d994cb5",
+          "clientsecret": "NP0XXXXXXXXXXXXXXXXXXX",
+          "tenantid": "18910a48-e492-4c49-8043-3449f7964bd6"
+        }
+      }
+    }
+  }
+}
+
+

The “Application ID” value is placed as “Client ID” in the configuration file. You can also see that in the example above, we have “NewAccounts” set to false in the default MeshCentral domain, but set to true in the Azure section. This indicates that new accounts are not allowed in this domain except if it’s a new user that is authenticating thru Azure. Once done, restart the MeshCentral server. Depending on your setup, you many need to run MeshCentral once manually to allow new required modules to be installed. Once running again, you should see the Azure single sign-on button on the login page.

+

+

JumpCloud Authentication using SAML

+

While using OAuth may be interesting, it’s more likely that MeshCentral servers used in an enterprise environment will want to use SAML (Security Assertion Markup Language). This is a widely deployed sign-on protocol used in enterprises so that, for example, employees can login to many different web sites using a single set of company credentials. MeshCentral can be one of many web sites that some users may want to log into.

+

In this section, we setup MeshCentral with JumpCloud, an easy to use sign-in provider. You can create an account on JumpCloud for free with up to 10 users allowing you to quickly get setup and test the following setup. In the next section, we look at a generic SAML configuration.

+

Before getting started with this section, make sure your server is on the Internet and publicly available and that it has a valid TLS certificate. You can use Let’s Encrypt to get a valid TLS certificate. Then, start by going to https://jumpcloud.com and creating an administrator account. Once setup, go to “Applications” and click on the big plug sign to create a new application.

+

+

You will need to create a custom SAML application by clicking the “Custom SAML App”.

+

+

Then, you can fill in the form with an application name and logo.

+
    +
  • For the IdP Entity ID, put “jumpcloud”.
  • +
  • For the SP Entity ID put “meshcentral”.
  • +
  • For the ACS URL, put the callback URL of your server. In this case it will be “https://(yourservername)/auth-jumpcloud-callback”
  • +
  • Lastly in the attributes section, add 3 user attribute mapping.
      +
    • “firstname” to “firstname”
    • +
    • “lastname” to “lastname”
    • +
    • “email” to “email”
    • +
    +
  • +
+

The attribute mappings will allow MeshCentral to receive from JumpCloud the first and last name of the user and the email address of the use. If any of these values are changed in the future, MeshCentral will update them the next time the user logs into MeshCentral. Here is an example configuration with red arrows next to important values.

+

+

Once setup, you will need to allow one or more users to use the new application. One way to do this is to just add your new application to the “All Users” group.

+

+

We are now almost done with JumpCloud. The last thing we need to do is download the certificate that JumpCloud will be using to sign the SAML assertions. You can get this certificate by going in the “Applications” tab, click on your new application and select “Download Certificate” as shown here.

+

+

Save the certificate as “jumpcloud-saml.pem” and place it in the “meshcentral-data” folder. You are now ready to configure MeshCentral. Edit the config.json and make it look like this:

+
{
+  "settings": {
+    "Cert": "myserver.mydomain.com",
+    "Port": 443,
+    "RedirPort": 80
+  },
+  "domains": {
+    "": {
+      "Title": "MyServer",
+      "Title2": "Servername",
+      "NewAccounts": false,
+      "authStrategies": {
+        "jumpcloud": {
+          "__callbackurl": "https://server/auth-jumpcloud-callback",
+          "NewAccounts": true,
+          "entityid": "meshcentral",
+          "idpurl": "https://sso.jumpcloud.com/saml2/saml2",
+          "cert": "jumpcloud-saml.pem"
+        }
+      }
+    }
+  }
+}
+
+

Take note that the “entityid”, “idpurl” and “cert” are values taken from JumpCloud. The callback URL should be configured in JumpCloud as we have done in previous steps. You can see that in the example above, we have “NewAccounts” set to false in the default MeshCentral domain, but set to true in the JumpCloud section. This indicates that new accounts are not allowed in this domain except if it’s a new user that is authenticating thru JumpCloud.

+

You are now ready to restart the MeshCentral server. Extra modules will be needed to support SAML and so, depending on your server configuration, you may need to run MeshCentral manually once to allow the new modules to be installed from NPM. Once restarted, you should see the JumpCloud sign-in button on the login screen.

+

+

Users can sign-in using the regular username and password or using JumpCloud.

+

Generic SAML setup

+

In this section, we look at configuring SAML with a generic authentication provider. The setup is exactly the same as with JumpCloud in the previous section, but we will be using a different section in the config.json to that a generic login icon is shown on the login page.

+

A generic SAML setup will look like this:

+
{
+  "settings": {
+    "Cert": "myserver.mydomain.com",
+    "Port": 443,
+    "RedirPort": 80
+  },
+  "domains": {
+    "": {
+      "Title": "MyServer",
+      "Title2": "Servername",
+      "NewAccounts": 1,
+      "authStrategies": {
+        "saml": {
+          "__callbackurl": "https://server/auth-saml-callback",
+          "entityid": "meshcentral",
+          "idpurl": "https://server/saml2",
+          "cert": "saml.pem"
+        }
+      }
+    }
+  }
+}
+
+

The callback URL will be of the form “https://(servername)/auth-saml-callback”. You should set the entityid, idpurl as given by the identity provider. Lastly, place the identity provider certificate file in the “meshcentral-data” folder and indicate the name of the file in “cert”. Once setup, restart the server and you should see a Single Sign-on button on the login screen.

+

+

Enabling SAML will require MeshCentral to install extra modules from NPM, so depending on your server configuration, you may need to run MeshCentral once manually.

+
+

Note

+

MeshCentral only supports "POST". For example Authentik's default setting is to use "Redirect" as a "Service Provider Binding".

+
+

Generic OpenID Connect Setup

+

Generally, if you are using an IdP that supports OpenID Connect (OIDC), you can use a very basic configuration to get started, and if needed, add more specific or advanced configurations later. Here is what your config file will look like with a basic, generic, configuration.

+
{
+    "settings": {
+        "cert": "mesh.your.domain",
+        "port": 443,
+        "sqlite3": true
+    },
+    "domains": {
+        "": {
+            "title": "Mesh",
+            "title2": ".Your.Domain",
+            "authStrategies": {
+                "oidc": {
+                    "issuer": "https://sso.your.domain",
+                    "clientid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
+                    "clientsecret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+                    "newAccounts": true
+                }
+            }
+        }
+  }
+}
+
+

As you can see, this is roughly the same as all the other OAuth2 based authentication strategies. These are the basics you need to get started using OpenID Connect because it's still authenticating with OAuth2. If you plan to take advantage of some of the more advanced features provided by this strategy you should consider reading the additional strategy documentation.

+
+

NOTE: MeshCentral will use https://mesh.your.domain/auth-oidc-callback as the default redirect uri.

+
+

Improvements to MeshCentral

+

In 2007, the first version of MeshCentral was built. We will refer to it as “MeshCentral1”. When MeshCentral1 was designed, HTML5 did not exist and web sockets where not implemented in any of the major browsers. Many design decisions were made at the time that are no longer optimal today. With the advent of the latest MeshCentral, MeshCentral1 is no longer supported and MeshCentral v2 has been significantly redesigned and mostly re-written based of previous version. Here is a list of improvements made in MeshCentral when compared with MeshCentral1:

+
    +
  • Quick Installation – By having MeshCentral published on NPM (www.npmjs.com) it’s now easy to download and install MeshCentral on both Linux and Windows*. On Linux* you can use NPM directly (“npm install meshcentral”) and on Windows you can use the .MSI installer.
  • +
  • Cross-Platform Support – Contrary to MeshCentral1 that only runs on Windows*, MeshCentral can run on any environment that supports NodeJS. This includes Windows*, Linux* and OSX*. Because MeshCentral runs on Linux, it often lowers hosting costs and makes it possible to run MeshCentral in a Docker* container environment.
  • +
  • Runs with Little Compute Resources – Typical MeshCentral1 installation requires a large disk space foot print (approx* 30G of disk space) and is compute intensive even for small deployments. MeshCentral requires little resources to host (70MB) and able to deliver reasonable performance on a 900Mhz CPU with 1GB RAM.
  • +
  • Multi-Tenancy and Load Balancing Support – MeshCentral can handle hosting many server instances at once. Each instance or “domain” has it’s own administrators, users and computers to manage. The server can handle each instance using a url path “server.com/customer1” or a DNS name “customer1.server.com”. Many customers can be handled by having all the DNS names point to the same server IP address. MeshCentral will take care of serving the right TLS certificate for each connection.
  • +
  • Single Executable – MeshCentral is a single-module or single executable server. All of the components of MeshCentral1 including IIS, Swarm, AJAX, Social, Manageability Servers are all build into one single executable. This makes it super easy to setup and run, it also minimizes problems and overhead caused by having many components communicate to each other. When the server is updated, all of the components are updated at once and effective.
  • +
  • Web Application Design – MeshCentral1 has 100’s of web pages and often times a click on a web page causes the browser to load a different web page and this creates more load on the server. With MeshCentral there are only two main web pages: The login page and the main web application. This design is much more responsive since the server now delegates most of the UI workload to the client’s web browser.
  • +
  • Real-Time User Interface – In MeshCentral, the user never has to hit the “refresh” button to update the web page. The web interface is completely real-time and updates as things change. MeshCentral uses websockets to connect to the server and get real-time events.
  • +
  • Single Programming Language – MeshCentral1 used JavaScript on the browser, C# on the server and C for the agent. Use of 3 different programming languages means that developers wanting to implement a new use-case needs to have sufficient skills to change between these 3 languages during the coding session. Makes the code significantly more difficult to understand and maintain.
  • +
  • Support for LAN only Mode – MeshCentral is capable of being setup as “LAN only” mode. In fact, this is the default mode when no static name or IP address is provided. In this mode, MeshAgents perform a multicast search on the network for the server making a static DNS/IP unnecessary.
  • +
  • Support for TLS Offloaders – TLS offloaders are now fully supported. This means that MeshCentral can handle way more network connections and traffic significantly.
  • +
  • Support for CIRA User/Pass Login – MeshCentral now supports both Intel AMT CIRA user/pass login and certificate login. Compared to MeshCentral1 that only supported certificate login, user/pass login is easier to setup and it can also be used for TLS offloaders and CIRA authentication.
  • +
  • No Live State Stored in the Database – One if the big problems with MeshCentral1 is that a lot of the live states (Agent, User and AMT connections and disconnections) needed to be stored in the database. This caused a few problems, first the extra load on the database that was un-necessary, but also that servers did not have real-time state information about other servers (they had to query the database). This resulted in more load on the database and scaling issues. In MeshCentral, all live states are kept in the RAM which boosts performance significantly.
  • +
  • Agentless Intel AMT Support – With MeshCentral1, administrators have to install the MeshAgent software on all computers, even if it was only for used for Intel AMT. MeshCentral supports a new agent-less mesh type that allows administrators to just setup the server strictly for Intel AMT only.
  • +
  • Latest Security & Crypto algorithms – MeshCentral uses all the latest cryptographic algorithm, notably SHA384 and RSA3072 making it more resistant to future quantum computer attacks. This would be very difficult to retrofit into MeshCentralv1 since it would require change of database schema and 1000’s of line of code thus making the server incompatible with the current version version, making migration difficult.
  • +
  • Support for Email Verification and Password Recovery – MeshCentral can be configured with an SMTP server to send out e-mail confirmation messages and password recovery message. This is an important feature that was missing in MeshCentral1.
  • +
  • MeshInterceptor Support – MeshCentral can insert HTTP and Intel AMT redirection credential into a live data stream. This is useful to allow an administrator to securely pass Intel AMT password and control over an Intel AMT computer via web browser without the additional administrator login UI.
  • +
+

It’s possible to perform migration to MeshCentral from MeshCentral1 server using a migration package. The MeshCentral Migration Tool will convert your existing user database into a format that can be imported into MeshCentral.

+

+

In addition to the migration tool, MeshCentral has a special module that will update all MeshAgents from v1 to v2 so the transition should be simple.

+

Additional Resources

+

In addition to this document, there are a growing set of MeshCentral tutorial videos available on YouTube which covers all of the basic at www.meshcommander.com/meshcentral2/tutorials. The tutorial includes videos on how to perform server installation using both the Windows MSI installer and NPM methods.

+

+

Conclusion

+

MeshCentral is a free, open source and powerful remote management solution that is cross-platform. In this document, we have covered in detail on how to install and configure MeshCentral server to meet specific environment and use-case. MeshCentral works in many environments and situations. MeshCentral is not only simple to install but also takes minimal resources to host which makes it a very good remote management solution. As with any good software, MeshCentral will continue to be updated and evolve.

+

License

+

MeshCentral and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0.

+

Annex 1: Sample Configuration File

+

In this annex, we present a complete sample config.json file. You would put this file in the “meshcentral-data” folder that is created when MeshCentral is first run. The config.json is completely optional and the server will run with default values with it. All key names in this file are case insensitive.

+
{
+  "settings": {
+    "MongoDb": "mongodb://127.0.0.1:27017/meshcentral",
+    "MongoDbCol": "meshcentral",
+    "Port": 4430,
+    "AliasPort": 443,
+    "RedirPort": 800,
+    "TlsOffload": "127.0.0.1",
+    "MpsPort": 44330,
+    "MpsAliasPort": 4433,
+    "MpsTlsOffload": true,
+    "SessionTime": 30,
+    "SessionKey": "MyReallySecretPassword",
+    "AllowLoginToken": true,
+    "AllowFraming": true,
+    "WebRTC": true,
+    "ClickOnce": true
+  },
+  "domains": {
+    "": {
+      "Title": "MyServer",
+      "Title2": "Servername",
+      "TitlePicture": "title-sample.png",
+      "UserQuota": 1048576,
+      "MeshQuota": 248576,
+      "NewAccounts": true,
+      "Footer": "<a href='https://twitter.com/mytwitter'>Twitter</a>",
+      "PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1 }
+    },
+    "customer1": {
+      "Dns": "customer1.myserver.com",
+      "Title": "Customer1",
+      "Title2": "TestServer",
+      "NewAccounts": 1,
+      "Auth": "sspi",
+      "Footer": "Test"
+    },
+    "info": {
+      "share": "C:\\ExtraWebSite"
+    }
+  },
+  "letsencrypt": {
+    "email": "myemail@myserver.com ",
+    "names": "myserver.com,customer1.myserver.com",
+    "rsaKeySize": 3072,
+    "production": false
+  },
+  "peers": {
+    "serverId": "server1",
+    "servers": {
+      "server1": { "url": "wss://192.168.2.133:443/" },
+      "server2": { "url": "wss://192.168.1.106:443/" }
+    }
+  },
+  "smtp": {
+    "host": "smtp.myserver.com",
+    "port": 25,
+    "from": "myemail@myserver.com",
+    "tls": false
+  }
+}
+
+

All these values are examples only, this config.json should just be used as an example and none of the values here are real.

+

Annex 2: Tips & Tricks

+

In this annex, we present various suggestions. These are often found by users on the GitHub community and readers are encouraged to participate. The GitHub community is at: https://github.com/Ylianst/MeshCentral/issues

+

Remote Terminal

+

When doing a remote terminal session to a Linux computer, it may be interesting to run the bash shell under a different user. One would typically use the command:

+
su -s /bin/bash myOtherUser
+
+

However, because bash is not run in interactive mode, the command line prompt may be empty and history keys (up and down), tab and backspace will not work right. The correct command is:

+
su -c '/bin/bash -i' myOtherUser
+
+

This will run bash in interactive mode and work correctly.

+

SSH and SFTP integration to the Terminal

+

MeshCentral has built-in web-based integration of SSH in the "Terminal" tab and SFTP in the "Files" tab.

+
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/openidConnectStrategy/index.html b/meshcentral/openidConnectStrategy/index.html new file mode 100644 index 00000000..27876f19 --- /dev/null +++ b/meshcentral/openidConnectStrategy/index.html @@ -0,0 +1,3525 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + openidConnectStrategy - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Using the OpenID Connect Strategy on MeshCentral

+

Overview

+

Introduction

+

There is a lot of information to go over, but first, why OpenID Connect?

+

Esentially its because its both based on a industry standard authorization protocol, and is becoming an industry standard authentication protocol. Put simply it's reliable and reusable, and we use OpenID Connect for exactly those reasons, almost every everyone does, and we want to be able to integrate with almost anyone. This strategy allows us to expand the potential of MeshCentral through the potential of OpenID Connect.

+

In this document, we will learn about the OpenID Connect specification at a high level, and then use that information to configure the OpenID Connect strategy for MeshCentral using a generic OpenID Connect compatible IdP. After that we will go over some advanced configurations and then continue by explaining how to use the new presets for popular IdPs, specifically Google or Azure. Then we will explore the configuration and usage of the groups feature.

+
+

ATTENTION: As of MeshCentral v1.1.22 there are multiple config options being depreciated. Using any of the old configs will only generate a warning in the authlog and will not stop you from using this strategy at this time. If there is information found in both the new and old config locations the new config location will be used. We will go over the specifics later, now lets jump in.

+
+

Chart of Frequently Used Terms and Acronyms

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TermAKADescriptions
OAuth 2.0OAuth2OAuth 2.0 is the industry-standard protocol for user authorization.
OpenID ConnectOIDCIdentity layer built on top of OAuth2 for user authentication.
Identity ProviderIdPThe service used to provide authentication and authorization.
Preset ConfigsPresetsSet of pre-configured values to allow some specific IdPs to connect correctly.
OAuth2 ScopeScopeA flag requesting access to a specific resource or endpoint
OIDC ClaimClaimA returned property in the user info provided by your IdP
User AuthenticationAuthNChecks if you are who you say you are. Example: Username and password authentication
User AuthorizationAuthZCheck if you have the permissions required to access a specific resource or endpoint
+

OpenID Connect Technology Overview

+

OpenID Connect is a simple identity layer built on top of the OAuth2 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an “Authorization Server”, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.

+

OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and logout, when it makes sense for them.

+

That description was straight from OpenID Connect Documentation, but basically, OAuth2 is the foundation upon which OpenID Connect was built, allowing for wide ranging compatability and interconnection. OpenID Connect appends the secure user authentication OAuth2 is known for, with user authorization by allowing the request of additional scopes that provide additional claims or access to API's in an easily expandable way.

+

Annotations

+

Own IDP, CA and Docker

+

If you operate your own identity provider, your own certification authority and MeshCentral via Docker, it is necessary to provide the complete certificate chain, otherwise NodeJS (in particular the openid-client module) will refuse the connection to the IDP server.

+

The following errors can be found in the log file:

+
+

OIDC: Discovery failed.

+

UNABLE_TO_GET_ISSUER_CERT_LOCALLY

+
+

To solve this problem, the certificate chain in PEM format must be placed in the data directory and the following entry must be added to the docker-compose.yml file in the “environment” section: +

    environment:
+            - NODE_EXTRA_CA_CERTS=/opt/meshcentral/meshcentral-data/chain.pem
+

+

Basic Config

+

Introduction

+

Generally, if you are using an IdP that supports OIDC, you can use a very basic configuration to get started, and if needed, add more specific or advanced configurations later. Here is what your config file will look like with a basic, generic, configuration.

+

Basic Config File Example

+
{
+    "settings": {
+        "cert": "mesh.your.domain",
+        "port": 443,
+        "sqlite3": true
+    },
+    "domains": {
+        "": {
+            "title": "MeshCentral",
+            "title2": "Your sub-title",
+            "authStrategies": {
+                "oidc": {
+                    "issuer": "https://sso.your.domain",
+                    "clientid": "2d5685c5-0f32-4c1f-9f09-c60e0dbc948a",
+                    "clientsecret": "7PiGSLSLL4e7NGi67KM229tfK7Z7TqzQ",
+                    "newAccounts": true
+                }
+            }
+        }
+    }
+}
+
+

As you can see, this is roughly the same as all the other OAuth2 based authentication strategies. These are the basics you need to get started, however, if you plan to take advantage of some of the more advanced features provided by this strategy, you'll need to keep reading.

+

In this most basic of setups, you only need the URL of the issuer, as well as a client ID and a client secret. Notice in this example that the callback URL (or client redirect uri) is not configured, thats because MeshCentral will use https://mesh.your.domain/auth-oidc-callback as the default. Once you've got your configuration saved, restart MeshCentral and you should see an OpenID Connect Single Sign-on button on the login screen.

+
+

WARNING: The redirect endpoint must EXACTLY match the value provided to your IdP or your will deny the connection.

+

ATTENTION: You are required to configure the cert property in the settings section for the default domain, and configure the dns property under each additional domain.

+
+

Advanced Options

+

Overview

+

There are plenty of options at your disposal if you need them. In fact, you can configure any property that node-openid-client supports. The openid-client module supports far more customization than I know what to do with, if you want to know more check out node-openid-client on GitHub for expert level configuration details. There are plenty of things you can configure with this strategy and there is a lot of decumentation behind the tools used to make this all happen. I strongly recommend you explore the config schema, and if you have a complicated config maybe check out the openid-client readme. Theres a list of resources at the end if you want more information on any specific topics. In the meantime, let’s take a look at an example of what your config file could look with a slightly more complicated configuration, including multiple manually defined endpoints.

+

Advanced Config File Example

+
{
+    "settings": {
+        "cert": "mesh.your.domain",
+        "port": 443,
+        "redirPort": 80,
+        "AgentPong": 300,
+        "TLSOffload": "192.168.1.50",
+        "SelfUpdate": false,
+        "AllowFraming": false,
+        "sqlite3": true,
+        "WebRTC": true
+    },
+    "domains": {
+        "": {
+            "title": "Mesh",
+            "title2": ".Your.Domain",
+            "orphanAgentUser": "~oidc:e48f8ef3-a9cb-4c84-b6d1-fb7d294e963c",
+            "authStrategies": {
+                "oidc": {
+                    "issuer": {
+                        "issuer": "https://sso.your.domain",
+                        "authorization_endpoint": "https://auth.your.domain/auth-endpoint",
+                        "token_endpoint": "https://tokens.sso.your.domain/token-endpoint",
+                        "end_session_endpoint": "https://sso.your.domain/logout",
+                        "jwks_uri": "https://sso.your.domain/jwks-uri"
+                    },
+                    "client": {
+                        "client_id": "110d5612-0822-4449-a057-8a0dbe26eca5",
+                        "client_secret": "4TqST46K53o3Z2Q88p39YwR6YwJb7Cka",
+                        "redirect_uri": "https://mesh.your.domain/auth-oidc-callback",
+                        "post_logout_redirect_uri": "https://mesh.your.domain/login",
+                        "token_endpoint_auth_method": "client_secret_post",
+                        "response_types": "authorization_code"
+                    },
+                    "custom": {
+                        "scope": [ "openid", "profile", "read.EmailAlias", "read.UserProfile" ],
+                        "preset": null
+                    },
+                    "groups": {
+                        "recursive": true,
+                        "required": ["Group1", "Group2"],
+                        "siteadmin": ["GroupA", "GroupB"],
+                        "revokeAdmin": true,
+                        "sync": { 
+                            "filter": ["Group1", "GroupB", "OtherGroup"]
+                        },
+                        "claim": "GroupClaim",
+                        "scope": "read.GroupMemberships"
+                    },
+                    "logouturl": "https://sso.your.domain/logout?r=https://mesh.your.domain/login",
+                    "newAccounts": true
+                },
+                {...}
+            }
+        }
+    }
+}
+
+

"Issuer" Options

+

Introduction

+

In the advanced example config above, did you notice that the issuer property has changed from a string to an object compared to the basic example? This not only allows for much a much smaller config footprint when advanced issuer options are not required, it successfully fools you in to a false sense of confidence early on in this document. If you are manually configuring the issuer endpoints, keep in mind that MeshCentral will still attempt to discover ALL issuer information. Obviously if you manually configure an endpoint, it will be used even if the discovered information is different from your config.

+
+

NOTE: If you are using a preset, you dont need to define an issuer. If you do, the predefined information will be ignored.

+
+

Common Config Chart

+ + + + + + + + + + + + + + + + + + + +
NameDescriptionDefaultExampleRequired
issuerThe primary URI that represents your Identity Providers authentication endpoints.N/A"issuer": "https://sso.your.domain"
"issuer": { "issuer": "https://sso.your.domain" }
Unless using preset.
+

Advanced Config Example

+
"issuer": {
+   "issuer": "https://sso.your.domain",
+   "authorization_endpoint": "https://auth.your.domain/auth-endpoint",
+   "token_endpoint": "https://tokens.sso.your.domain/token-endpoint",
+   "end_session_endpoint": "https://sso.your.domain/logout",
+   "jwks_uri": "https://sso.your.domain/jwks-uri"
+},
+
+

Required and Commonly Used Configs

+

The issuer property in the issuer object is the only one required, and its only required if you aren't using a preset. Besides the issuer, these are mostly options related to the endpoints and their configuration. The schema below looks intimidating but it comes down to being able to support any IdP. Setting the issuer, and end_session_endpoint are the two main ones you want to setup.

+

Schema

+
"issuer": { 
+    "type": ["string","object"],
+    "format": "uri",
+    "description": "Issuer options. Requires issuer URI (issuer.issuer) to discover missing information unless using preset",
+    "properties": {
+        "issuer": { "type": "string", "format": "uri", "description": "URI of the issuer." },
+        "authorization_endpoint": { "type": "string", "format": "uri" },
+        "token_endpoint": { "type": "string", "format": "uri" },
+        "jwks_uri": { "type": "string", "format": "uri" },
+        "userinfo_endpoint": { "type": "string", "format": "uri" },
+        "revocation_endpoint": { "type": "string", "format": "uri" },
+        "introspection_endpoint": { "type": "string", "format": "uri" },
+        "end_session_endpoint": {
+            "type": "string",
+            "format": "uri",
+            "description": "URI to direct users to when logging out of MeshCentral.",
+            "default": "this.issuer/logout"
+            },
+        "registration_endpoint": { "type": "string", "format": "uri" },
+        "token_endpoint_auth_methods_supported": { "type": "string" },
+        "token_endpoint_auth_signing_alg_values_supported": { "type": "string" },
+        "introspection_endpoint_auth_methods_supported": { "type": "string" },
+        "introspection_endpoint_auth_signing_alg_values_supported": { "type": "string" },
+        "revocation_endpoint_auth_methods_supported": { "type": "string" },
+        "revocation_endpoint_auth_signing_alg_values_supported": { "type": "string" },
+        "request_object_signing_alg_values_supported": { "type": "string" },
+        "mtls_endpoint_aliases": {
+            "type":"object",
+            "properties": {
+                "token_endpoint": { "type": "string", "format": "uri" },
+                "userinfo_endpoint": { "type": "string", "format": "uri" },
+                "revocation_endpoint": { "type": "string", "format": "uri" },
+                "introspection_endpoint": { "type": "string", "format": "uri" }
+            }
+        }
+    },
+    "additionalProperties": false
+},
+
+

"Client" Options

+

Introduction

+

There are just about as many option as possible here since openid-client also provides a Client class, because of this you are able to manually configure the client how ever you need. This includes setting your redirect URI to any available path, for example, if I was using the "google" preset and wanted to have Google redirect me back to "https://mesh.your.domain/oauth2/oidc/redirect/givemebackgooglemusicyoujerks", MeshCentral will now fully support you in that. One of the other options is the post logout redirect URI, and it is exactly what it sounds like. After MeshCentral logs out a user using the IdPs end session endpoint, it send the post logout redirect URI to your IdP to forward the user back to MeshCentral or to an valid URI such as a homepage.

+
+

NOTE: The client object is required, however an exception would be with using old configs, which will be discussed later.

+
+

Common Configs

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionDefaultExampleRequired
client_idThe client ID provided by your Identity Provider (IdP)N/Abdd6aa4b-d2a2-4ceb-96d3-b3e23cd17678true
client_secretThe client secret provided by your Identity Provider (IdP)N/AvUg82LJ322rp2bvdzuVRh3dPn3oVo29mtrue
redirect_uri"URI your IdP sends you after successful authorization.https://mesh.your.domain/auth-oidc-callbackhttps://mesh.your.domain/oauth2/oidc/redirectfalse
post_logout_redirect_uriURI for your IdP to send you after logging out of IdP via MeshCentral.https://mesh.your.domain/loginhttps://site.your.other.domain/loginfalse
+

Advanced Config Example

+
"client": {
+    "client_id": "00b3875c-8d82-4238-a8ef-25303fa7f9f2",
+    "client_secret": "7PP453H577xbFDCqG8nYEJg8M3u8GT8F",
+    "redirect_uri": "https://mesh.your.domain/auth-oidc-callback",
+    "post_logout_redirect_uri": "https://mesh.your.domain/login",
+    "token_endpoint_auth_method": "client_secret_post",
+    "response_types": "authorization_code"
+},
+
+

Required and Commonly Used Configs

+

There are many available options you can configure but most of them go unused. Although there are a few commonly used properties. The first two properties, client_id and client_secret are required. The next one redirect_uri is used to setup a custom URI for the redirect back to MeshCentral after being authenicated by your IdP. The post_logout_redirect_uri property is used to tell your IdP where to send you after being logged out. These work in conjunction with the issuers end_session_url to automatically fill in any blanks in the config.

+

Schema

+
"client": { 
+    "type": "object",
+    "description": "OIDC Client Options",
+    "properties": {
+        "client_id": { 
+            "type": "string",
+            "description": "REQUIRED: The client ID provided by your Identity Provider (IdP)"
+        },
+        "client_secret": {
+            "type": "string",
+            "description": "REQUIRED: The client secret provided by your Identity Provider (IdP)"
+        },
+        "redirect_uri": {
+            "type": "string",
+            "format": "uri",
+            "description": "URI your IdP sends you after successful authorization. This must match what is listed with your IdP. (Default is https://[currentHost][currentPath]/auth-oidc-callback)"
+        },
+        "post_logout_redirect_uri": {
+            "type": "string",
+            "format": "uri",
+            "description": "URI for your IdP to send you after logging out of IdP via MeshCentral.",
+            "default": "https:[currentHost][currentPath]/login"
+        },
+        "id_token_signed_response_alg": { "type": "string", "default": "RS256" },
+        "id_token_encrypted_response_alg": { "type": "string" },
+        "id_token_encrypted_response_enc": { "type": "string" },
+        "userinfo_signed_response_alg": { "type": "string" },
+        "userinfo_encrypted_response_alg": { "type": "string" },
+        "userinfo_encrypted_response_enc": { "type": "string" },
+        "response_types": { "type": ["string", "array"], "default": ["code"] },
+        "default_max_age": { "type": "number" },
+        "require_auth_time": { "type": "boolean", "default": false }, 
+        "request_object_signing_alg": { "type": "string" },
+        "request_object_encryption_alg": { "type": "string" },
+        "request_object_encryption_enc": { "type": "string" },
+        "token_endpoint_auth_method": {
+            "type": "string",
+            "default": "client_secret_basic",
+            "enum": [ "none", "client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt" ]
+        }, 
+        "introspection_endpoint_auth_method": {
+            "type": "string",
+            "default": "client_secret_basic",
+            "enum": [ "none", "client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt" ]
+        }, 
+        "revocation_endpoint_auth_method": {
+            "type": "string",
+            "default": "client_secret_basic",
+            "enum": [ "none", "client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt" ]
+        }, 
+        "token_endpoint_auth_signing_alg": { "type": "string" },
+        "introspection_endpoint_auth_signing_alg": { "type": "string" },
+        "revocation_endpoint_auth_signing_alg": { "type": "string" },
+        "tls_client_certificate_bound_access_tokens": { "type": "boolean" }
+    },
+    "required": [ "client_id", "client_secret" ],
+    "additionalProperties": false
+},
+
+

"Custom" Options

+

Introduction

+

These are all the options that dont fit with the issuer or client, including the presets. The presets define more than just the issuer URL used in discovery, they also define API endpoints, and specific ways to assemble your data. You are able to manually override most of the effects of the preset, but not all. You are able to manually configure the scope of the authorization request though, as well as choose which claims to use if your IdP uses something other than the defaults.

+
+

NOTE: The scope must be a string, an array of strings, or a space separated list of scopes as a single string.

+
+

Common Config Chart

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionDefaultExampleRequired
scopeA list of scopes to request from the issuer."openid profile email"["openid", "profile"]false
claimsA group of claims to use instead of the defaultsDefauts to name of property except that uuid used sub"claims": {"uuid": "unique_name"}false
+

Advanced Config Example

+
"custom": {
+    "scope": [ "openid", "profile", "read.EmailAlias", "read.UserProfile" ],
+    "preset": null,
+    "claims": {
+        "name": "nameOfUser",
+        "email": "publicEmail"
+    }
+},
+
+
+

NOTE: You can preset to null if you want to explicitly disable presets.

+
+

Required and Commonly Used Configs

+

As should be apparent by the name alone, the custom property does not need to be configured and is used for optional or advanced configurations. With that said, lets look at few common options strategy will default to using the openid, profile, and email scopes to gather the required information about the user, if your IdP doesn't support or require all these, you can set up the scope manually. Combine that with the ability to set the group scope and you can end up with an entirely custom scope being sent to your IdP. Not to mention the claims property, which allows you to pick and choose what claims to use to gather your data in case you have issues with any of the default behaviors of OpenID Connect and your IdP. This is also where you would set the preset and any values required by the presets.

+

Schema

+
"custom": {
+    "type": "object",
+    "properties": {
+        "scope": {
+            "type": ["string", "array"],
+            "description": "A list of scopes to request from the issuer.",
+            "default": "openid profile email",
+            "examples": ["openid", ["openid", "profile"], "openid profile email", "openid profile email groups"]
+        },
+        "claims": {
+            "type": "object",
+            "properties": {
+                "email": { "type": "string" },
+                "name": { "type": "string" },
+                "uuid": { "type": "string" }
+            }
+        },
+        "preset": { "type": "string", "enum": ["azure", "google"]},
+        "tenant_id": { "type": "string", "description": "REQUIRED FOR AZURE PRESET: Tenantid for Azure"},
+        "customer_id": { "type": "string", "description": "REQUIRED FOR GOOGLE PRESET IF USING GROUPS: Customer ID from Google, should start with 'C'."}
+    },
+    "additionalProperties": false
+},
+
+

"Groups" Options

+

Introduction

+

The groups option allows you to use the groups you already have with your IdP in MeshCentral in a few ways. First you can set a group that the authorized user must be in to sign in to MeshCentral. You can also allow users with the right memberships automatic admin privlidges, and there is even an option to revoke privlidges if the user is NOT in the admin group. Besides these filters, you can filter the sync property to mirror only certain groups as MeshCentral User Groups, dynamically created as the user logs in. You can of course simply enable sync and mirror all groups from your IdP as User Groups. Additionally you can define the scope and claim of the groups for a custom setup, again allowing for a wide range of IdPs to be used, even without a preset.

+

Common Config Chart

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionDefaultExampleRequired
syncAllows you to mirror user groups from your IdP.false"sync": { "filter": ["Group1", "Group2"] }
"sync": true
false
requiredAccess is only granted to users who are a member
of at least one of the listed required groups.
undefined"required": ["Group1", "Group2"]false
siteadminFull site admin priviledges will be granted to users
who are a member of at least one of the listed admin groups
undefined"siteadmin": ["Group1", "Group2"]false
revokeAdminIf true, admin privileges will be revoked from users
who arent a member of at least one of the listed admin groups.
true"revokeAdmin": falsefalse
+

Advanced Config Example

+
"groups": {
+    "recursive": true,
+    "required": ["Group1", "Group2"],
+    "siteadmin": ["GroupA", "GroupB"],
+    "revokeAdmin": false,
+    "sync": { 
+        "filter": ["Group1", "GroupB", "OtherGroup"]
+    },
+    "claim": "GroupClaim",
+    "scope": "read.GroupMemberships"
+},
+
+

Required and Commonly Used Configs

+

As you can see in the schema below, there aren't any required properties in the groups object, however there are some commonly used ones. The first, and maybe most commonly used one, is the sync property. The sync property mirrors IdP provided groups into MeshCentral as user groups. You can then configure access as required to those groups, and as users log in, they will be added to the now existing groups if they are a member. You also have other options like using a custom scope or claim to get your IdP communicating with MeshCentral properly, without the use of preset configs. You also can set the required property if you need to limit authorization to users that are a member of at least one of the groups you set. or the siteadmin property to grant admin privilege, with the revokeAdmin property available to allow revoking admin rights also.

+

Schema

+
"groups": {
+  "type": "object",
+  "properties": {
+    "recursive": {
+      "type": "boolean",
+      "default": false,
+      "description": "When true, the group memberships will be scanned recursively."
+    },
+    "required": {
+      "type": [ "string", "array" ],
+      "description": "Access is only granted to users who are a member of at least one of the listed required groups."
+    },
+    "siteadmin": {
+      "type": [ "string", "array" ],
+      "description": "Full site admin priviledges will be granted to users who are a member of at least one of the listed admin groups."
+    },
+    "revokeAdmin": {
+      "type": "boolean",
+      "default": false,
+      "description": "If true, admin privileges will be revoked from users who are NOT a member of at least one of the listed admin groups."
+    },
+    "sync": {
+      "type": [ "boolean", "object" ],
+      "default": false,
+      "description": "If true, all groups found during user login are mirrored into MeshCentral user groups.",
+      "properties": {
+        "filter": {
+          "type": [ "string", "array" ],
+          "description": "Only groups listed here are mirrored into MeshCentral user groups."
+        }
+      }
+    },
+    "scope": { "type": "string", "default": "groups", "description": "Custom scope to use." },
+    "claim": { "type": "string", "default": "groups", "description": "Custom claim to use." }
+  },
+  "additionalProperties": false
+}
+
+

Preset OpenID Connect Configurations

+

Overview

+

Introduction

+

Google is a blah and is used by tons of blahs as its so great. Lets move on.

+

Common Config Chart

+
+

NOTE: All settings directly related to presets are in the custom section of the config.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionExampleRequired
presetManually enable the use of a preset."preset": "google"
"preset": "azure"
false
customer_idCustomer ID of the Google Workspaces instace you
plan to use with the groups feature.
"customer_id": ["Group1", "Group2"]If google preset is used with groups feature
tenant_idTenant ID from Azure AD, this is required to use
the azure preset as it is part of the issuer url.
"siteadmin": ["Group1", "Group2"]false
+

Google Preset

+

Prerequisites

+
+

Check out this documentation to get ready before we start.

+
+

Basic Config Example

+
"oidc": {
+    "client": {
+        "client_id": "268438852161-r8xa7qxwf3rr0shp1xnpgmm70bnag21p.apps.googleusercontent.com",
+        "client_secret": "ETFWBX-gFEaxfPXs1tWmAOkuWDFTgoL3nwh"
+    }
+}
+
+

Specifics

+

If you notice above I forgot to add any preset related configs, however because google tags the client ID we can detect that and automatically use the google preset. The above config is tested, the sentive data has been scrambled of course. That said, you would normally use this preset in more advaced setups, let take a look at an example.

+

Advanced Example with Groups

+
"oidc": {
+    "client": {
+        "client_id": "424555768625-k7ub3ovqs0yp7mfo0usvyyx51nfii61c.apps.googleusercontent.com",
+        "client_secret": "QLBCQY-nRYmjnFWv3nKyHGmwQEGLokP6ldk"
+    },
+    "custom": {
+        "preset": "google",
+        "customer_id": "C46kyhmps"
+    },
+    "groups": {
+        "siteadmin": ["GroupA", "GroupB"],
+        "revokeAdmin": true,
+        "sync": true
+    },
+    "callbackURL": "https://mesh.your.domain/auth-oidc-google-callback"
+},
+
+

Customer ID and Groups

+

As always, the client ID and secret are required, the customer ID on the other hand is only required if you plan to take advantage of the groups function and the google preset. This also requires you have a customer ID, if you have do, it is available in the Google Workspace Admin Console under Profile->View. Groups work the same as they would with any other IdP but they are pulled from the Workspace groups.

+

Schema

+
"custom": {
+    "type": "object",
+    "properties": {
+        "preset": { "type": "string", "enum": ["azure", "google"]},
+        "customer_id": { "type": "string", "description": "Customer ID from Google, should start with 'C'."}
+    },
+    "additionalProperties": false
+},
+
+

Azure Preset

+

Prerequisites

+

To configure OIDC-based SSO, you need an Azure account with an active subscription. Create an account for free. The account used for setup must be of the following roles: Global Administrator, Cloud Application Administrator, Application Administrator, or owner the service principal.

+
+

Check this documentation for more information.

+
+

Basic Config Example

+
"oidc": {
+    "client": {
+        "client_id": "a1gkl04i-40g8-2h74-6v41-2jm2o2x0x27r",
+        "client_secret": "AxT6U5K4QtcyS6gF48gndL7Ys22BL15BWJImuq1O"
+    },
+    "custom": {
+        "preset": "azure",
+        "tenant_id": "46a6022g-4h33-1451-h1rc-08102ga3b5e4"
+    }
+}
+
+

Specifics

+

As with all other types of configuration for the OIDC strategy, the Azure preset requires a client ID and secret.The tenant ID is used as part of the issuer URI to make even the most basic AuthN requests so it is also required for the azure preset. besides that groups are available to the Azure preset as well as the recursive feature of groups. This allows you to search user groups recursively for groups they have membership in through other groups.

+
+

NOTE: The Azure AD preset uses the Tenant ID as part of the issuer URI:
"https://login.microsoftonline.com/" + strategy.custom.tenant_id + "/v2.0"

+
+

Advanced Example with Groups

+
"oidc": {
+    "client": {
+        "client_id": "a1gkl04i-40g8-2h74-6v41-2jm2o2x0x27r",
+        "client_secret": "AxT6U5K4QtcyS6gF48gndL7Ys22BL15BWJImuq1O"
+    },
+    "custom": {
+        "preset": "azure",
+        "tenant_id": "46a6022g-4h33-1451-h1rc-08102ga3b5e4"
+    },
+    "groups": {
+        "recursive": true,
+        "siteadmin": ["GroupA", "GroupB"],
+        "revokeAdmin": true,
+        "sync": true
+    },
+    "callbackURL": "https://mesh.your.domain/auth-oidc-azure-callback"
+},
+
+

Schema

+
"custom": {
+    "type": "object",
+    "properties": {
+        "preset": { "type": "string", "enum": ["azure", "google"]},
+        "tenant_id": { "type": "string", "description": "Tenant ID from Azure AD."}
+    },
+    "additionalProperties": false
+},
+
+

Depreciated Properties

+

Overview

+

Introduction

+

As of MeshCentral v1.1.22 and the writing of this documentation, the node module that handles everything was changed from passport-openid-connect to openid-client. As a result of this change, multiple properties in the config have been depcrecated; this means some options in the strategy arent being used anymore. These are often referred to as "old configs" by this documentation.

+

Migrating Old Configs

+

We upgraded but what about all the existing users, we couldn't just invalidate every config pre v1.1.22. So in an effort to allow greater flexibility to all users of MeshCentral, and what futures scholars will all agree was an obvious move, all the depreciated configs will continue working as expected. Using any of the old options will just generate a warning in the authlog and will not stop you from using this the OIDC strategy with outdated configs, however if both the equivalent new and old config are set the new config will be used.

+

Old Config Example

+
"oidc": {
+    "newAccounts": true,
+    "clientid": "421326444155-i1tt4bsmk3jm7dri6jldekl86rfpg07r.apps.googleusercontent.com",
+    "clientsecret": "GNLXOL-kEDjufOCk6pIcTHtaHFOCgbT4hoi"
+}
+
+

This example was chosen because I wanted to highlight an advantage of supporting these old configs long term, even in a depreciated status. That is, the ability to copy your existing config from one of the related strategies without making any changes to your config by using the presets. This allows you to test out the oidc strategy without commiting to anything, since the user is always appended with the strategy used to login. In this example, the config was originally a google auth strategy config, changing the "google" to "oidc" is all that was done to the above config, besides obsfuscation of course.

+

Advcanced Old Config Example

+
"oidc": {
+    "authorizationURL": "https://sso.your.domain/api/oidc/authorization",
+    "callbackURL": "https://mesh.your.domain/oauth2/oidc/callback",
+    "clientid": "tZiPTMDNuSaQPapAQJtwDXVnYjjhQybc",
+    "clientsecret": "vrQWspJxdVAxEFJdrxvxeQwWkooVcqdU",
+    "issuer": "https://sso.your.domain",
+    "tokenURL": "https://sso.your.domain/api/oidc/token",
+    "userInfoURL": "https://sso.your.domain/api/oidc/userinfo",
+    "logoutURL": "https://sso.your.domain/logout?rd=https://mesh.your.domain/login",
+    "groups": {
+        "recursive": true,
+        "required": ["Group1", "Group2"],
+        "siteadmin": ["GroupA", "GroupB"],
+        "sync": { 
+            "filter": ["Group1", "GroupB", "OtherGroup"]
+        }
+    },
+    "newAccounts": true
+},
+
+

Upgrading to v1.1.22

+

If you were already using a meticulusly configured oidc strategy, all of your configs will still be used. You will simply see a warning in the logs if any depreciated properties were used. If you check the authLog there are additional details about the old config and provide the new place to put that information. In this advanced config, even the groups will continue to work just as they did before without any user intervention when upgrading from a version of MeshCentral pre v1.1.22. There are no step to take and no action is needed, moving the configs to the new locations is completely optional at the moment.

+

Links

+

https://cloud.google.com/identity/docs/reference/rest/v1/groups/list

+

https://www.onelogin.com/learn/authentication-vs-authorization

+

https://auth0.com/docs/authenticate/protocols/openid-connect-protocol

+

https://github.com/panva/node-openid-client

+

https://openid.net/connect/

+
+

You just read openidConnectStrategy.ms v1.0.1 by @mstrhakr

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/plugins/index.html b/meshcentral/plugins/index.html new file mode 100644 index 00000000..0030becf --- /dev/null +++ b/meshcentral/plugins/index.html @@ -0,0 +1,1794 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Plugins - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Plugins - Installation & Usage

+
+

Note

+

Plugins as such receive no support by the main developers of MeshCentral. If you experience problems with MeshCentral please make sure to disable all plugins before further troubleshooting!

+
+

Use Cases

+

Certain feature requests may not be suitable for all MeshCentral users and thus are available as a plugin. Furthermore users can develop their own plugins - as described further below - to extend functionality or benefit from integrating MeshCentral into their existing application environment.

+

List of publically available plugins

+

https://github.com/topics/meshcentral-plugin

+

Installation of a plugin

+
    +
  1. First please make sure that you enable plugins in the configuration
    +

    "plugins": { + "enabled": true +},

    +
    +
  2. +
  3. Restart MeshCentral if you needed to change the configuration.
  4. +
  5. Log into MeshCentral as full administrator.
  6. +
  7. Go my My Server -> Plugins, then hit the Download plugin button.
  8. +
  9. A dialog opens requesting a URL, e.g. put in: https://github.com/ryanblenis/MeshCentral-ScriptTask
  10. +
  11. The plugin pops up in the plugin list below the download button, you can now configure and enable/disable it.
  12. +
+

Plugins - Development & Hooks

+
+

Note

+

Plugins as such receive no support by the main developers of MeshCentral. If you experience problems with MeshCentral please make sure to disable all plugins before further troubleshooting!

+
+

Overview

+

Not all feature requests may be suitable for all MeshCentral users and thus can't be integrated into MeshCentral directly. Hwoever, Instead of maintaining a complete fork of MeshCentral it is much easier to extend MeshCentral's functionality using hooks and writing plugins for it.

+

Anatomy of a plugin:

+
- plugin_name/
+-- config.json
+-- plugin_name.js
+-- modules_meshcore/ // optional
+--- plugin_name.js  // optional
+
+

Plugin Configuration File

+

A valid JSON object within a file named config.json in the root folder of your project. An example:

+
{
+  "name": "Plugin Name",
+  "shortName": "plugin_name",
+  "version": "0.0.0",
+  "author": "Author Name",
+  "description": "Short Description of the plugin",
+  "hasAdminPanel": false,
+  "homepage": "https://www.example.com",
+  "changelogUrl": "https://raw.githubusercontent.com/User/Project/master/changelog.md",
+  "configUrl": "https://raw.githubusercontent.com/User/Project/master/config.json",
+  "downloadUrl": "https://github.com/User/Project/archive/master.zip",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/User/Project.git"
+  },
+  "versionHistoryUrl": "https://api.github.com/repos/User/Project/tags",
+  "meshCentralCompat": ">0.4.3"
+}
+
+

Configuration File Properties

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldRequiredTypeDescription
nameYesstringa human-readable name for the plugin
shortNameYesstringan alphanumeric, unique short identifier for the plugin (will be used to access your functions throughout the project
versionYesstringthe current version of the plugin
authorNostringthe author's name
descriptionYesstringa short, human-readable description of what the plugin does
hasAdminPanelYesbooleantrue or false, indicates whether or not the plugin will offer its own administrative interface
homepageYesstringthe URL of the projects homepage
changelogUrlYesstringthe URL to the changelog of the project
configUrlYesstringthe URL to the config.json of the project
downloadUrlYesstringthe URL to a ZIP of the project (used for installation/upgrades)
repositoryYesJSON objectcontains the following attributes
repository.typeYesstringvalid values are git and in the future, npm will also be supported.
repository.urlYesstringthe URL to the project's repository
versionHistoryUrlNostringthe URL to the project's versions/tags
meshCentralCompatYesstringthe minimum version string of required compatibility with the MeshCentral server, can be formatted as "0.1.2-c" or ">=0.1.2-c". Currently only supports minimum version, not full semantic checking.
+

Plugin Hooks

+

In essence, hooks are locations in the code which enable developers to tap into a module to either provide alternative behavior or to respond to an event.

+

These are separated into the following categories depending on the type of functionality the plugin should offer.

+
    +
  • Web UI, to modify the MeshCentral admin interface
  • +
  • Back End, to modify core functionality of the server and communicate with the Web UI layer as well as the Mesh Agent (Node) layer to send commands and data
  • +
  • Mesh Agent (Node), to introduce functionality to each agent
  • +
+

Web UI Hooks

+
    +
  • onDeviceRefreshEnd: called when a device is selected in the MeshCentral web interface
  • +
  • registerPluginTab: callable when a device is selected in the MeshCentral web interface to register a new tab for plugin data, if required. Accepts an object, or function that returns an object, with the following properties: { tabId: "yourShortNameHere", tabTitle: "Your Display Name"}. A tab and div with the associated ID and title will be created for your use
  • +
  • onDesktopDisconnect: called when a remote desktop session is disconnected
  • +
  • onWebUIStartupEnd: called when the page has loaded for the first time after a login / refresh
  • +
  • goPageStart: called before page changes take effect. Passes 2 arguments ( : int, : Event)
  • +
  • goPageEnd: called after page changes take effect. Passes 2 arguments ( : int, : Event)
  • +
+

Exports

+

Any function can be exported to the Web UI layer by adding the name of the function to an exports array in the plugin object.

+

Back End Hooks

+
    +
  • server_startup: called once when the server starts (or when the plugin is first installed)
  • +
  • hook_agentCoreIsStable: called once when an agent initially checks in
  • +
  • hook_processAgentData: called each time an agent transmits data back to the server
  • +
  • hook_userLoggedIn: called when a user has logged into the web interface
  • +
  • hook_setupHttpHandlers: called before all http handlers are setup
  • +
+

Mesh Agent

+

Use of the optional file plugin_name.js in the optional folder modules_meshcore will include the file in the default meshcore file sent to each endpoint. This is useful to add functionality on each of the endpoints.

+

Structure

+

Much of MeshCentral revolves around returning objects for your structures, and plugins are no different. Within your plugin you can traverse all the way up to the web server and MeshCentral Server classes to access all the functionality those layers provide. This is done by passing the current object to newly created objects, and assigning that reference to a parent variable within that object.

+

Versioning

+

Versioning your plugin correctly and consistently is essential to ensure users of your plugin are prompted to upgrade when it is available. Semantic versioning is recommended.

+

Changelog

+

A changelog is highly recommended so that your users know what's changed since their last version.

+

Sample Plugin

+

MeshCentral-Sample is a simple plugin that, upon disconnecting from remote desktop, prompts the user to enter a manual event (note), pre-filled in with the date and timestamp.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/security/index.html b/meshcentral/security/index.html new file mode 100644 index 00000000..c3a1422a --- /dev/null +++ b/meshcentral/security/index.html @@ -0,0 +1,1598 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Security - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Security

+

Rate Limiting login attempts

+

You can use the MeshCentral Server Console with the command badlogins to see the current settings.

+

Adjust these items in your config.json

+
"settings": {
+    "_maxInvalidLogin": {
+    "time": 10,
+    "count": 10,
+    "coolofftime": 10
+    },
+}
+
+

+

Disabling TLS 1.0/1.1 for AMT

+
{
+  "settings": {
+    "mpshighsecurity": true
+  }
+}
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/tipsntricks/index.html b/meshcentral/tipsntricks/index.html new file mode 100644 index 00000000..47ea1de0 --- /dev/null +++ b/meshcentral/tipsntricks/index.html @@ -0,0 +1,1641 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tips n Tricks - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Tips n' Tricks

+

Colors in SSH

+

The SSH terminal does support color. The issue is going to be the terminal configuration of the shell. Try typing this:

+
ls -al --color /tmp
+
+

Fancy config editing with VS Code

+

A common problem in the issues is an incorrect config.json. What makes a config incorrect? How can you verify your config is correct?

+

Easy! Use Visual Studio Code to edit your config.json and add the schema at the top.

+

If you haven't already, download VS code. +Download or copy the config.json to your computer. +Open config.json in code and add the schema as the top line. This schema is the raw JSON file in the MeshCentral repo.

+
{
+  "$schema": "https://raw.githubusercontent.com/Ylianst/MeshCentral/master/meshcentral-config-schema.json",
+  "settings": {
+    "your settings go here": "..."
+  }
+}
+
+

Now you have autocomplete, auto-format and validation for your config.json! If you start typing, Code will show the values that are valid for the location you are editing. Words with a red squiggle line are errors. Words with a orange squiggle line are warnings. Hover over both to see the error message and possible fixes. Code can even format your config.

+

While this is a huge step up, it's not perfect. If you notice, there are some invalid keys in the screenshot. This is perfectly valid JSON and MeshCentral will ignore them (maybe?). If you paste some configs into the wrong section, code will not tell you it's in the wrong section. Autocomplete will tell you what keys are valid and the type of the value (i.e. string, number, boolean).

+

Hopefully this will help verify your config is syntactically correct and prevent needless formatting errors, misspellings, etc.

+

+

Downloading Folders

+

If you would like to download folders via Files simply select folder/files then use the zip and download the zip file by clicking on it.

+

Share device groups with AD logins

+

If you would like to share device groups with different AD users.

+

In the config.json set "ldapuserkey" to "sAMAccountName".

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcentral/tokens/index.html b/meshcentral/tokens/index.html new file mode 100644 index 00000000..4f714bc2 --- /dev/null +++ b/meshcentral/tokens/index.html @@ -0,0 +1,1594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tokens - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

14.1 Tokens

+

User Tokens

+

User Tokens 1

+

User Tokens 2

+

Software Integration Tokens

+
+

Warning

+

You can only have a SINGLE loginTokenKey for your meshcentral server!
+So if you regenerate a loginTokenKey, the old one will be revoked/deleted!

+
+

You can create/view the Login Token Key with the following:

+
node node_modules/meshcentral --loginTokenKey
+
+

You can then reset/revoke/renew the Login Token Key with the following to create a new one:

+
node node_modules/meshcentral --loginTokenKey --loginTokenGen
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshcmd/images/2022-05-15-15-11-39.png b/meshcmd/images/2022-05-15-15-11-39.png new file mode 100644 index 00000000..c04e32f3 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-11-39.png differ diff --git a/meshcmd/images/2022-05-15-15-21-12.png b/meshcmd/images/2022-05-15-15-21-12.png new file mode 100644 index 00000000..3d1224de Binary files /dev/null and b/meshcmd/images/2022-05-15-15-21-12.png differ diff --git a/meshcmd/images/2022-05-15-15-22-07.png b/meshcmd/images/2022-05-15-15-22-07.png new file mode 100644 index 00000000..346689db Binary files /dev/null and b/meshcmd/images/2022-05-15-15-22-07.png differ diff --git a/meshcmd/images/2022-05-15-15-23-45.png b/meshcmd/images/2022-05-15-15-23-45.png new file mode 100644 index 00000000..cc833b92 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-23-45.png differ diff --git a/meshcmd/images/2022-05-15-15-24-12.png b/meshcmd/images/2022-05-15-15-24-12.png new file mode 100644 index 00000000..a2c22954 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-24-12.png differ diff --git a/meshcmd/images/2022-05-15-15-27-41.png b/meshcmd/images/2022-05-15-15-27-41.png new file mode 100644 index 00000000..93d81ab9 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-27-41.png differ diff --git a/meshcmd/images/2022-05-15-15-30-02.png b/meshcmd/images/2022-05-15-15-30-02.png new file mode 100644 index 00000000..5af14c19 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-30-02.png differ diff --git a/meshcmd/images/2022-05-15-15-30-41.png b/meshcmd/images/2022-05-15-15-30-41.png new file mode 100644 index 00000000..f1ae36d2 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-30-41.png differ diff --git a/meshcmd/images/2022-05-15-15-32-15.png b/meshcmd/images/2022-05-15-15-32-15.png new file mode 100644 index 00000000..38e712aa Binary files /dev/null and b/meshcmd/images/2022-05-15-15-32-15.png differ diff --git a/meshcmd/images/2022-05-15-15-37-16.png b/meshcmd/images/2022-05-15-15-37-16.png new file mode 100644 index 00000000..5e0847bf Binary files /dev/null and b/meshcmd/images/2022-05-15-15-37-16.png differ diff --git a/meshcmd/images/2022-05-15-15-39-40.png b/meshcmd/images/2022-05-15-15-39-40.png new file mode 100644 index 00000000..465fff15 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-39-40.png differ diff --git a/meshcmd/images/2022-05-15-15-40-05.png b/meshcmd/images/2022-05-15-15-40-05.png new file mode 100644 index 00000000..fe8b4ca8 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-40-05.png differ diff --git a/meshcmd/images/2022-05-15-15-41-20.png b/meshcmd/images/2022-05-15-15-41-20.png new file mode 100644 index 00000000..21e428c9 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-41-20.png differ diff --git a/meshcmd/images/2022-05-15-15-42-01.png b/meshcmd/images/2022-05-15-15-42-01.png new file mode 100644 index 00000000..02244403 Binary files /dev/null and b/meshcmd/images/2022-05-15-15-42-01.png differ diff --git a/meshcmd/index.html b/meshcmd/index.html new file mode 100644 index 00000000..09965db9 --- /dev/null +++ b/meshcmd/index.html @@ -0,0 +1,2158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MeshCmd - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

MeshCmd

+

MeshCmd Guide as .pdf as .odt

+

Video Walkthru

+
+ +
+ +

+

Abstract

+

This user guide contains all essential information for the user to make full use of MeshCmd, a command line tool used to perform tasks on MeshCentral and for Intel® AMT. This tool run on Windows and Linux and perform a wide array of different tasks. From routing traffic over the Internet to activating Intel AMT, MeshCmd is a great do it all tool for computer management.

+

Introduction

+

MeshCmd, called “Mesh Command”, is a command line tool that runs on both Windows and Linux and used to perform many tasks related to computer management. As the tool continues to evolve, it will continue to be improved and acquire more features. Broadly, the tool is intended to perform three sets of tasks.

+

MeshCentral2 command line operations +- There are command line operations that relate to interacting with the MeshCentral2 server. A good example of this is to route traffic from your computer to a remote computer on the internet thru a MeshCentral2 server.

+

Intel AMT local actions +- If you happen to have Intel AMT on your computer, MeshCmd can take a look at the version and activation status, activate and de-activate Intel AMT and help with getting access to Intel AMT and more.

+

Intel AMT remote actions +- Whether you have Intel AMT on your local computer or a remote computer on your network, MeshCmd can help unlock the features Intel AMT provides. From getting the state of Intel AMT remotely to running configuration scripts and loading MeshCommander into Intel AMT web storage.

+

To get started, you need to download MeshCmd for your computer. MeshCmd is a single file executable that you can get on MeshCommander.com at: http://www.meshcommander.com/meshcommander/meshcmd

+

+If you have access to a MeshCentral2 server, the download link to MeshCmd is at the bottom left of the main device page.

+

+

Once you click on the “MeshCmd” link, a dialog box will allow you to select the operating system you want to get a link to MeshCmd. MeshCentral will also provide an action file called meshaction.txt that contains information on how MeshCmd can connect back to that MeshCentral server. This is optional, and only used for some operations.

+

Once downloaded, just run it from the command prompt or terminal window.

+
C:\Temp>meshcmd
+MeshCentral Command (MeshCmd)
+No action specified, use MeshCmd like this:
+
+  meshcmd [action] [arguments...]
+
+Valid MeshCentral actions:
+  Route             - Map a local TCP port to a remote computer.
+
+Valid local actions:
+  SMBios            - Display System Management BIOS tables for this computer.
+  RawSMBios         - Display RAW System Management BIOS tables for this computer.
+  MicroLMS          - Run MicroLMS, allowing local access to Intel AMT.
+  AmtInfo           - Show Intel AMT version and activation state.
+  AmtVersions       - Show all Intel ME version information.
+  AmtHashes         - Show all Intel AMT trusted activation hashes.
+  AmtCCM            - Activate Intel AMT into Client Control Mode.
+  AmtACM            - Activate Intel AMT into Admin Control Mode.
+  AmtDeactivate     - Deactivate Intel AMT if activated in Client Control mode.
+  AmtAcmDeactivate  - Deactivate Intel AMT if activated in Admin Control mode.
+
+Valid local or remote actions:
+  MeshCommander     - Launch a local MeshCommander web server.
+  AmtUUID           - Show Intel AMT unique identifier.
+  AmtAuditLog       - Show the Intel AMT audit log.
+  AmtLoadWebApp     - Load MeshCommander in Intel AMT 11.6+ firmware.
+  AmtClearWebApp    - Clear everything from Intel AMT web storage.
+  AmtStorageState   - Show contents of the Intel AMT web storage.
+  AmtSaveState      - Save all Intel AMT WSMAN object to file.
+  AmtPresence       - Heartbeat a local Intel AMT watchdog agent.
+  AmtScript         - Run .mescript on Intel AMT.
+  AmtIDER           - Mount local disk image to remote computer.
+
+Help on a specific action using:
+
+  meshcmd help [action]
+
+

By default you will get the help screen with all of the different actions you can take with the tool. You can also get help by typing “help” followed by the action name. In this document we will cover the main actions that MeshCmd can perform. If you don’t use Intel AMT at all, then only the MeshCentral actions are interesting for you. If you use Intel AMT, the rest of the actions will be of interest to you.

+

MeshCentral TCP port mapping

+

MeshCmd can map a TCP port from your local computer to any remote port on any computer with one of your MeshAgents installed. This port mapping will work over a local network or the Internet and should work even thru proxies and firewalls.

+

In order to start using MeshCmd in this way, you first need to have access to a MeshCentral server and at least one computer you already manage. TCP port mapping works by selecting a port on your local computer that will be routed to a remote device and port. Here, local port 123 is routed thru the server to port 123 on a remote device.

+

+

Of course, this picture is a bit simplify. Firewalls, NAT routers and HTTP proxies may be in the way and the MeshAgent on the remote computer will act at the TCP traffic relay in most cases.

+

One typical use of this is to route local port 1234 to port Microsoft RDP port 3389 on a remote device. Once routed, you can start a RDP session on “localhost:1234” and get a RDP session to the remote device. The RDP protocol is feature rich and efficient, so you get a great user experience regardless of where in the world the remote computer is at.

+

To get started, click on a device in MeshCentral and click on the “Router” link on the bottom left of the device page.

+

+

You can download MeshCmd is you have not done so already, but more importantly, download the action.txt file. The file is in text format and contain something like this:

+
{
+ "action": "route",
+ "localPort": 1234,              Change this
+ "remoteName": "AmtMachine7",
+ "remoteNodeId": "node//@yw$s5jLUivpzZ49laprt4T0sBaOKImbDAiniothQwccZPukCB696$BvPWAW0Bg2",
+ "remotePort": 3389,                 Change this
+ "username": "admin",
+ "password": "",                 Note that the password is empty
+ "serverId": "D99362D5ED8BAEA8BF9E743B34B242256370C460FD66CB62373C6CFCB204D6D70
+7403E396CF0EF6DC2B3A42F735135FD",
+ "serverHttpsHash": "D9DE9E27A229B5355708A3672FB23237CC994A680B3570D242A91E36B4AE5BC
+96539E59746E2B71EEF3DBDABBF2AE138",
+ "debugLevel": 0,
+ "serverUrl": "wss://devbox.mesh.meshcentral.com:443/meshrelay.ashx"
+}
+
+

The action file contains almost all the parameters needed to perform the route. It indicates the local and remote ports, the remote computer unique identifier, server location and authentication information and more. You can just put the action file in the same folder as MeshCmd and run MeshCmd, it will automatically pick up the arguments from the meshaction.txt file.

+

You may want to change the local and remote port in the action file to suite your needs. Be default, the Microsoft RDP port is the target. If the password is not specified in the meshaction.txt file, you can also specify it as a meshcmd argument.

+
C:\MeshCmd>meshcmd --pass xxxxxxxx 
+Redirecting local port 1234 to AmtMachine7:3389. Press ctrl-c to exit. 
+Now, the traffic router is ready. You can now RDP to localhost:1234 and login to the remote computer. 
+
+

MeshCommander

+

MeshCommander is a web based Intel AMT management console. MeshCmd has no less then three different versions of MeshCommander built-in, so if you are using Intel AMT, it’s worth a moment to get some knowledge about MeshCommander which is available as a standalone tool along with a full user’s guide at:http://www.meshcommander.com/and also included as part of MeshCentral.

+

The three versions included in MeshCmd are:

+
    +
  • MeshCommander as a local web server.
  • +
  • MeshCommander for LMS
  • +
  • MeshCommander for firmware.
  • +
+

In this section, we review the three versions, how they are used and what can be done with them.

+

MeshCommander local web server

+

You can start MeshCommander on a local web server by typing “meshcmd meshcommander”. By default, local port 3000 is used, but you can optionally specify the port using “--localport [port]”. Running it with look like this:

+
C:\MeshCmd>meshcmd meshcommander
+MeshCommander running on HTTP port 3000. Ctrl-C to exit. 
+
+

Once running, use a web browser and go to “http://localhost:3000” to see the MeshCommander web page. The page will start out without any computers in it and you will have to add some.

+

+

In this mode, the local computers that are added will be stored in the browser’s storage cache. So, clearing the browser’s cache will also clear the list of computers. You can however load and save the list of computers using the “Open…” and “Save…” buttons. This version of MeshCommander will have some limitations when compared to the full version installed using the Windows .MSI installer. Notably:

+
    +
  • No certificate management or validation.
  • +
  • Mutual-Authenticated TLS is not supported.
  • +
  • Kerberos authentication is not supported.
  • +
  • IDE-R is not supported.
  • +
+

This said, features like KVM, Terminal and most Intel AMT configuration options are available which makes this a fairly powerful Intel AMT management console. You can run MeshCommander as a background server on both Windows and Linux. To do this, use the install, uninstall, start and stop commands like this:

+
C:\MeshCmd>meshcmd meshcommander install
+Installing to "C:\Program Files (x86)\Open Source\MeshCmd\MeshCommander.exe" MeshCommander installed. 
+
+C:\MeshCmd>meshcmd meshcommander start
+MeshCommander starting.
+
+

On Linux computers, both systemd and initd are supported. When installing, the MeshCmd executable will be copied to a different installation folder.

+

LMS & MeshCommander

+

The Layered Management Service (LMS) is a background process that runs in the operating system and provides local OS access to Intel AMT. More specifically, it redirects local TCP ports 16992 and 16993 to Intel AMT thru the MEI driver. One way to check if the LMS service is installed on your computer is to tryhttp://localhost:16992 or https://localhost:16993in a browser. A page should generally show up.

+

On Windows, LMS is a Windows service that must be downloaded and installed as part of the OEM drivers for a computer. On Linux, it’s generally never installed and users normally have to download the source code and compile it themselves.

+

MeshCmd has its own LMS implementation, so is LMS is not installed it will automatically use its own internal one. In addition, you can run MicroLMS alone by typing the following list while running as root or local administrator:

+
C:\MeshCmd>meshcmd microlms
+MicroLMS started, MeshCommander on HTTP/16994. 
+
+

This will start MicroLMS and as indicated, start MeshCommander on local port 16994 at the same time. In total, traffic redirection looks like this:

+

+

In addition to providing normal services, MicroLMS that is part of MeshCmd will also start a web server on port 16994 and allow a browser to access LMS MeshCommander, a special version of MeshCommander specially made to run in this situation.

+

MeshCommander for LMS will show up even if the computer has Intel AMT un-configured and offer the user the choice to activate Intel AMT and perform configuration actions. In the following screen, we see a computer activated in Client Control Mode. Because it’s in this mode, you can use the web interface to de-activate Intel AMT or login to perform additional configuration.

+

+

MicroLMS along with MeshCommander for LMS is very useful, especially on Linux as it offers a single tool to setup and configuration Intel AMT. If you wish you run MicroLMS without MeshCommander being available on port 16994, run MeshCmd with “--noconsole":

+
C:\MeshCmd>meshcmd microlms --noconsole
+MicroLMS started. 
+
+

You can run MicroLMS as a background server on both Windows and Linux. To do this, use the MicroLMS install, uninstall, start and stop commands like this:

+
C:\MeshCmd>meshcmd microlms install
+Installing to "C:\Program Files (x86)\Open Source\MeshCmd\MicroLMS.exe" 
+MicroLMS installed. 
+
+C:\MeshCmd>meshcmd microlms start
+MicroLMS starting. 
+
+

On Linux computers, both systemd and initd are supported. When installing, the MeshCmd executable will be copied to a different installation folder.

+

MeshCommander for firmware

+

MeshCmd also includes a surprising version of MeshCommander, the one you can load into the firmware of Intel AMT. Starting with Intel AMT 11.6, you can push into the small ~190k storage space of Intel AMT a replacement to the index.htm page served by the firmware on port 16992 & 16993. In the following picture, the left side is the original Intel AMT web page, the right is the replaced MeshCommander built to go in firmware.

+

+

The firmware version of MeshCommander has support for remote desktop, terminal and all sorts of Intel AMT usages and configuration. Probably the most surprising is that this entire page is between 40k and 100k depending on the version you select. It’s notable that with MeshCommander loaded into Intel AMT firmware, one does not need any other tool except for a browser to perform most Intel AMT maintenance operations.

+
C:\MeshCmd>meshcmd amtloadwebapp --host 192.168.2.144 --pass xxxxxxxx
+Uploading MeshCommander...
+Verifying MeshCommander...
+Done.
+
+

To get the current state of Intel AMT web storage, type this:

+
C:\MeshCmd>meshcmd amtstoragestate --host 192.168.2.144 --pass xxxxxxxx
+Storage State: {
+  "information": {
+    "version": 1,
+    "realms": 7765759,
+    "user": "admin"
+  },
+  "content": {
+    "index.htm": {
+      "size": 57246      Replacement index.htm with size.
+    }
+  }
+}
+
+

Here, a 57k index.htm replacement is present in the Intel AMT flash. You can clear the web storage, revering the web page back to the original like this:

+
C:\MeshCmd>meshcmd amtclearwebapp --host 192.168.2.144 --pass xxxxxxxx
+Done.
+
+

When MeshCommander is loaded into Intel AMT, you can access it from a different computer using http://computername:16992 orhttps://computername:16993 You will need to authenticate first before getting access to the web page.

+

Intel AMT state & activation

+

MeshCmd can easily be used to read the local state of the computer and Intel AMT. There are many commands available to do this. The “SMBios” action works on most computers and is used to get basic information about your current system. The output is JSON format.

+
C:\MeshCmd>meshcmd smbios
+{
+  processorInfo: {
+    0: {
+      Processor: "CPU"
+      MaxSpeed: "3800 Mhz"
+      Cores: 4
+      Threads: 8
+      Populated: 1
+      Status: "Enabled"
+      Socket: "CPU 1"
+      Manufacturer: "Intel(R) Corporation"
+      Version: "Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz"
+    }
+  }
+  memoryInfo: {
+    location: "System Board"
+    maxCapacityKb: 33554432
+  }
+  systemInfo: {
+    uuid: "C57B83D9A94E11E18BA1505054503030"
+    wakeReason: "Power Switch"
+  }
+  systemSlots: {
+    uuid: "C57B83D9A94E11E18BA1505054503030"
+    wakeReason: "Power Switch"
+  }
+  amtInfo: {
+    AMT: true
+    enabled: true
+    storageRedirection: true
+    serialOverLan: true
+    kvm: true
+    TXT: false
+    VMX: true
+    MEBX: "66.0.0.8"
+    ManagementEngine: "8.1.0.57353"
+  }
+}
+
+

If you have Intel AMT on your system, you can use the “amtinfo”, “amtversions” and “amthashes” to get lots of information about Intel AMT current state. These commands use the Intel AMT MEI driver to get this information and require MeshCmd be run as root or administrator:

+
C:\MeshCmd>meshcmd amtinfo
+Intel AMT v8.1.71, activated in client control mode.
+Wired Enabled, DHCP, 00:1E:8C:F5:4F:ED, 192.168.2.10.
+
+C:\MeshCmd>meshcmd amtversions
+MEI Version = MKQ7710H.86A.0072.2017.0519.1347
+Flash = 8.1.71
+Netstack = 8.1.71
+AMTApps = 8.1.71
+AMT = 8.1.71
+Sku = 24584
+VendorID = 8086
+Build Number = 3608
+Recovery Version = 8.1.71
+Recovery Build Num = 3608
+Legacy Mode = False
+
+C:\MeshCmd>meshcmd amthashes
+VeriSign Class 3 Primary CA-G1, (Default, Active)
+  SHA256: E7685634EFACF69ACE939A6B255B7B4FABEF42935B50A265ACB5CB6027E44E70
+VeriSign Class 3 Primary CA-G3, (Default, Active)
+  SHA256: EB04CF5EB1F39AFA762F2BB120F296CBA520C1B97DB1589565B81CB9A17B7244
+…
+
+

In addition to getting Intel AMT state, MeshCmd can activate Intel AMT in client control mode (CCM) and de-activate Intel AMT if it’s in this mode. Doing this is very simple, starting with Intel AMT not being activated, you use the “amtccm” and “amtdeactivate” actions.

+
C:\MeshCmd>meshcmd amtinfo
+Intel AMT v8.1.71, pre-provisioning state.
+
+C:\MeshCmd>meshcmd amtccm --pass xxxxxxxx
+Success
+
+C:\MeshCmd>meshcmd amtinfo
+Intel AMT v8.1.71, activated in client control mode.
+
+C:\MeshCmd>meshcmd amtdeactivate
+Success
+
+

Note that when using the “amtccm” action, you need to provide a password that will be used for authentication into Intel AMT. This password must be strong with at least 8 characters including a lower case, an uppercase, a numeric value and a non-alpha-numeric value.

+

Intel AMT Audit Log

+

One very useful feature of MeshCmd is its ability to fetch the Intel AMT audit log. This can be valuable when doing forensics on a computer or just trying figure out what is being done thru Intel AMT. MeshCmd can pull the audit log on a local computer without any credentials, as long as it’s running as root or administrator, or pull the audit log remotely if usual credentials are provided.

+

+

The Intel AMT audit log will show when a computer’s Intel AMT was activated, when remote desktop sessions where initiated and more. To get the local audit log, just use the “AmtAuditLog” action.

+
C:\MeshCmd>meshcmd amtauditlog
+2004-01-01 19:17:58.000-08:00 - Local: Provisioning Started
+2018-01-26 14:03:16.000-08:00 - Local: Unprovisioning Started
+2018-01-26 14:03:31.000-08:00 - Local: Provisioning Started
+2018-02-01 12:13:14.000-08:00 - admin: KVM Enabled
+2018-02-01 12:16:01.000-08:00 - admin: KVM Session Started
+2018-02-01 12:16:07.000-08:00 - admin: KVM Session Ended
+2018-02-02 10:56:06.000-08:00 - admin: KVM Session Started
+2018-02-02 10:56:08.000-08:00 - admin: KVM Session Ended
+
+

To get a remote audit log:

+
C:\MeshCmd>meshcmd amtauditlog --host 192.168.2.144 --user admin --pass xxxxxxxx
+2003-12-31 23:06:58.000-08:00 - $$OsAdmin: Intel(r) ME Time Set
+2017-08-15 06:53:31.000-07:00 - $$OsAdmin: Intel(r) ME Time Set
+2017-10-13 17:55:11.000-07:00 - Local: Provisioning Started
+2017-10-13 17:55:54.000-07:00 - Local: Opt-In Policy Change
+2017-10-15 06:44:38.000-07:00 - admin: KVM Enabled
+
+

You can also save the audit log to file using the “--output" option.

+

Running Intel AMT script

+

MeshCmd has a full WSMAN stack built-in and can be used to run “.mescript” file on a target Intel AMT computer. Script file are useful when you want to run a set of actions on one or more Intel AMT computers at once. You can build a .mescript file using the script editor within MeshCommander.

+

This script editor allows the user to drag & drop script blocks, set parameters on each block and test the script against a connected Intel AMT computer.

+

+

In addition to building your own scripts, you can download a CIRA setup script from a MeshCentral server. When running this script, Intel AMT will be setup to call back to the server using an encrypted connection. This enables remote management of Intel AMT over the Internet. The CIRA setup script is available in the “Add CIRA” link for meshes that are Intel AMT only (no agent).

+

+

Once you got the script, run it with MeshCmd like this. You specify the host if it’s not localhost, the password and the script file.

+
C:\MeshCmd>meshcmd amtscript --host 192.168.2.106 --pass xxxxxxxx --script cira_setup.mescript
+Script Started
+Policies removed successfully
+Adding root certificate...
+Management Presence Server (MPS) successfully added
+Found matching (primary) mps: Intel(r) AMT:Management Presence Server 0
+Setting policy...
+Policy added successfully
+Remote Access user interfaces set to: BIOS & OS Enabed
+Parsing block parameters
+Setting Environment Detection
+Environment Detection set successfully
+Script Completed
+
+

In this example, the CIRA setup script was run on a remote computer. After the script is run, the computer got configured and connected back to the server. MeshCentral show the new connection state for this computer.

+

+

IDE Redirection

+

Video Walkthru

+
+ +
+ +

MeshCmd has all the code needed to perform Intel AMT IDE Redirection from the command line. This allows disk images on the administrator’s computer to be remotely mounted to an Intel AMT computer. You need to start with a floppy disk .img file and/or an .iso CDROM file.

+

+

Then use the “AmtIDER” command of MeshCMD to start an IDER session. The help command for AmtIDER looks like this:

+
C:\Temp>meshcmd help amtider
+AmtIDER will mount a local disk images to a remote Intel AMT computer. Example usage:
+
+  meshcmd amtider --host 1.2.3.4 --user admin --pass mypassword --tls --floppy disk.img --cdrom disk.iso
+
+Possible arguments:
+
+  --host [hostname]      The IP address or DNS name of Intel AMT.
+  --user [username]      The Intel AMT login username, admin is default.
+  --pass [password]      The Intel AMT login password.
+  --tls                  Specifies that TLS must be used.
+  --floppy [file]        Specifies .img file to be mounted as a flppy disk.
+  --cdrom [file]         Specifies .img file to be mounted as a CDROM disk.
+  --timeout [seconds]    Optional, disconnect after number of seconds without disk read.
+
+

The command is fairly simple. It takes as input a remote host, username/password for Intel AMT login, the disk images and TLS option. One can also specify the timeout option so that MeshCMD will automatically disconnect when no disk read operations are performed for a set number of seconds.

+

Conclusion

+

MeshCmd is a cross-platform command line tools that perform an ever-growing list of actions that are important for remote computer management. MeshCmd works alone or with MeshCentral and MeshCommander to offer a suite of free, opens source and powerful tools that work well together.

+

License

+

MeshCmd and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshctrl/images/2022-05-16-21-33-47.png b/meshctrl/images/2022-05-16-21-33-47.png new file mode 100644 index 00000000..80461cd0 Binary files /dev/null and b/meshctrl/images/2022-05-16-21-33-47.png differ diff --git a/meshctrl/images/2022-05-16-21-40-21.png b/meshctrl/images/2022-05-16-21-40-21.png new file mode 100644 index 00000000..e6c3c7c0 Binary files /dev/null and b/meshctrl/images/2022-05-16-21-40-21.png differ diff --git a/meshctrl/images/2022-05-16-21-47-32.png b/meshctrl/images/2022-05-16-21-47-32.png new file mode 100644 index 00000000..04286649 Binary files /dev/null and b/meshctrl/images/2022-05-16-21-47-32.png differ diff --git a/meshctrl/images/2022-05-16-21-50-39.png b/meshctrl/images/2022-05-16-21-50-39.png new file mode 100644 index 00000000..20898322 Binary files /dev/null and b/meshctrl/images/2022-05-16-21-50-39.png differ diff --git a/meshctrl/images/2022-05-16-21-54-38.png b/meshctrl/images/2022-05-16-21-54-38.png new file mode 100644 index 00000000..36be1c67 Binary files /dev/null and b/meshctrl/images/2022-05-16-21-54-38.png differ diff --git a/meshctrl/images/2022-05-16-22-04-06.png b/meshctrl/images/2022-05-16-22-04-06.png new file mode 100644 index 00000000..33995347 Binary files /dev/null and b/meshctrl/images/2022-05-16-22-04-06.png differ diff --git a/meshctrl/images/2022-05-16-22-06-35.png b/meshctrl/images/2022-05-16-22-06-35.png new file mode 100644 index 00000000..72916974 Binary files /dev/null and b/meshctrl/images/2022-05-16-22-06-35.png differ diff --git a/meshctrl/images/2022-05-16-22-20-44.png b/meshctrl/images/2022-05-16-22-20-44.png new file mode 100644 index 00000000..8eca8f1c Binary files /dev/null and b/meshctrl/images/2022-05-16-22-20-44.png differ diff --git a/meshctrl/images/2022-05-16-22-23-40.png b/meshctrl/images/2022-05-16-22-23-40.png new file mode 100644 index 00000000..2ff26eaf Binary files /dev/null and b/meshctrl/images/2022-05-16-22-23-40.png differ diff --git a/meshctrl/images/amt_commands.png b/meshctrl/images/amt_commands.png new file mode 100644 index 00000000..1f0ef3d3 Binary files /dev/null and b/meshctrl/images/amt_commands.png differ diff --git a/meshctrl/index.html b/meshctrl/index.html new file mode 100644 index 00000000..212add0b --- /dev/null +++ b/meshctrl/index.html @@ -0,0 +1,2199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MeshCtrl - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

MeshCtrl

+

MeshCmd Guide as .pdf as .odt

+

Video Walkthru

+
+ +
+ +

+

Abstract

+

This user guide contains all essential information for the user to make full use of MeshCtrl, a command line tool used to perform tasks on the MeshCentral server. This tool is built in NodeJS and can be run on the same computer as the server, or on a different computer. It’s typically used by administrators to perform various management operations on the MeshCentral server.

+

Introduction

+

MeshCtrl, also called “Mesh Control”, is a command line tool built in NodeJS that comes with MeshCentral and allows command line management operation of the MeshCentral server. It can be run both on the same computer as the MeshCentral server, or on a different computer. This tool allows an administrator to list user accounts, create and remove user accounts, list device groups and much more. All of the operations performed by this tool can also be performed using the MeshCentral website, however it’s sometimes useful to automate and script some management operations from the command line and this tool can do this.

+

+

MeshCtrl will login to the MeshCentral server using a web socket connection, just like the web application does and so, it needs to login to the server using user or administrator credentials. In the next section, we will cover how to login using MeshCtrl, we then follow with the operations that MeshCtrl can perform.

+

Running MeshCtrl

+

MeshCtrl.js is a file that is installed with MeshCentral and is located in the “./node_modules/meshcentral” folder. You can normally run MeshCtrl like this:

+
node ./node\_modules/meshcentral/meshctrl 
+
+

In this case, when the above command is run without parameters, you should see something like this:

+
MeshCtrl performs command line actions on a MeshCentral server.
+Information at: https://meshcommander.com/meshcentral
+No action specified, use MeshCtrl like this:
+
+  meshctrl [action] [arguments]
+
+Supported actions:
+  Help [action]             - Get help on an action.
+  ServerInfo                - Show server information.
+  UserInfo                  - Show user information.
+  ListUsers                 - List user accounts.
+  ListDevices               - List devices.
+  ListDeviceGroups          - List device groups.
+  ListUsersOfDeviceGroup    - List the users in a device group.
+  AddUser                   - Create a new user account.
+  RemoveUser                - Delete a user account.
+  AddDeviceGroup            - Create a new device group.
+  RemoveDeviceGroup         - Delete a device group.
+  AddUserToDeviceGroup      - Add a user to a device group.
+  RemoveUserFromDeviceGroup - Remove a user from a device group.
+  SendInviteEmail           - Send an agent install invitation email.
+  Broadcast                 - Display a message to all online users.
+
+Supported login arguments:
+  --url [wss://server]      - Server url, wss://localhost:443 is default.
+  --loginuser [username]    - Login username, admin is default.
+  --loginpass [password]    - Login password.
+  --token [number]          - 2nd factor authentication token.
+  --loginkey [hex]          - Server login key in hex.
+  --loginkeyfile [file]     - File containing server login key in hex.
+  --domain [domainid]       - Domain id, default is empty.
+
+

This indicated you have a good version of MeshCentral with support for MeshCtrl. MeshCentral and MeshCtrl will be updated together, so as changes are made to the server, MeshCtrl will also be updated to match.

+

The next step is to login to the server with MeshCtrl before we can start performing commands. For this document, we will assume we are connection to the local MeshCentral server on port 443 using TLS. For connecting to a different server, use the --url argument to set a different server.

+

Login

+

There are two ways to login to MeshCentral using MeshCtrl. You can use the normal username/password, or use a login key. In this section we look at both.

+

Login using username & password

+

This is the easiest way to login and start issuing commands is to login using a username and password.

+

+

You do this by using the "loginuser" and "loginpass" argument like this:

+
node ./node_modules/meshcentral/meshctrl serverinfo --loginuser "admin" --loginpass "mypassword"
+
+

In this example we are trying to run the “serverinfo” command using the “admin” user and “mypassword” as the password. Because “admin” if the default username for MeshCtrl, you can omit it and just type:

+
Authentication token required, use --token [number].
+
+

In this case, you need to use the Google Authenticator application or a similar application to enter your time based second factor like this:

+
node ./node_modules/meshcentral/meshctrl serverinfo --loginpass "mypassword" –token 123456
+
+

Once the command is run successfully, you should see a response like this:

+
name: devbox.meshcentral.com
+mpsname: devbox.meshcentral.com
+mpsport: 4433
+port: 443
+emailcheck: true
+domainauth: false
+tlshash: E7A41E1A56D8D716F7D7729B876E48693F2D19C4761F22B1…
+https: true
+redirport: 80
+
+

This is a typical response for the “serverinfo” command and a good way to test that everything is working as expected.

+

Login using the server login key

+

This technique needs some setup, but allows MeshCtrl to login as any account without knowing the password of the account, making it very powerful. Care must be taken when doing this and it’s recommended to only be done on the local server itself unless you know when you are doing.

+

+

In this technique, we will get a special encryption key from the server and use this to generate a login token to the server. First, we must activate the login token feature of the server by setting “AllowLoginToken” to “true” in the “settings” section of config.json:

+
{
+  "settings": {
+    "Port": 443,
+    "AllowLoginToken": true
+  }
+}
+
+

Then run the MeshCentral server with the --logintokenkey parameter, you should see something like this:

+
node ./node_modules/meshcentral --logintokenkey
+f3bd69a08a2dde4a3423bec8f20d8626b1e6365381f2d9919e7dfe256ace9cbbdea66bed2bdcd00b71eea9d7c727cb6eb37f3148c0c2d240d5ed44c8f3f6795a479c949159dad366571fea4db7f31c24
+
+

The hex value presented here is a sensitive secret that allows anyone to create login tokens that can be used for user impersonation. This key is used when MeshCentral does server peering and load balancing for example. You should save this key in a file, for example “key.txt”, you can do it like this:

+
node ./node_modules/meshcentral –logintokenkey > key.txt
+
+

Now that you have the server key, you can use MeshCtrl like this:

+
node ./node_modules/meshcentral/meshctrl serverinfo –-loginkeyfile key.txt --loginuser admin
+
+

Instead of using a password, we use the key file instead. MeshCtrl will generate a time limited cookie and use that to login as “admin”. Since “admin” is the default username for MeshCtrl, we can omit this parameter:

+
node ./node_modules/meshcentral/meshctrl serverinfo –-loginkeyfile key.txt
+
+

Like the username and password example above, the result will be the server information we requested. Something like this:

+
name: devbox.meshcentral.com
+mpsname: devbox.meshcentral.com
+mpsport: 4433
+port: 443
+emailcheck: true
+domainauth: false
+tlshash: E7A41E1A56D8D716F7D7729B876E48693F2D19C4761F22B1…
+https: true
+redirport: 80
+
+

Login into a different domain

+

So far, we have seen how to login to a MeshCentral server using a username/password or username/key technique for the default domain. MeshCentral supports many domains at once with each of the domain having its own set of users, groups and administrators.

+

+

To do this, using the username and password, we have to tell MeshCtrl to login using the URL that would be used to access that domain, for example:

+
node ./node_modules/meshcentral/meshctrl serverinfo --url wss://server/domainname --loginuser "admin" --loginpass "mypassword"
+
+

or for servers have a DNS name for a domain:

+
node ./node_modules/meshcentral/meshctrl serverinfo --url wss://domainname.server --loginuser "admin" --loginpass "mypassword"
+
+

Note the "--url" argument is different and so, the server will see MeshCtrl access the server using a different domain.

+

Doing a domain login using the login is key file is a bit different. We need to specify both the url to access that domain and specify the domain name explicitly using the --domain argument. For example:

+
node ./node_modules/meshcentral/meshctrl serverinfo –-loginkeyfile key.txt --url wss://server/domainname --domain mycustomer
+
+

So, we add both the url to access the correct domain and the domain name explicitly. The domain name must be exactly the one that is used in the domain section of the config.json for of MeshCentral. For example, if the MeshCentral config.json file looks like this:

+
{
+  "settings": {
+    "Cert": "myserver.com",
+    "Port": 443
+  },
+  "domains": {
+    "": {
+      "title": "Default domain",
+    },
+    "customer1": {
+      "dns": "c1.myserver.com",
+      "title": "Customer1 domain",
+    }
+  }
+}
+
+

The domain login for MeshCtrl with a key file would look like this:

+
node ./node_modules/meshcentral/meshctrl serverinfo –-loginkeyfile key.txt --url wss://c1.myserver.com --domain customer1
+
+

In the next section, we start making use of MeshCtrl to do useful things on the server. From now on, we will omit the login arguments, but note that they are required for all commands.

+

Making use of MeshCtrl

+

We can start doing useful operations with MeshCtrl. The current list of operations are:

+
edituser
+listusers
+listusersessions
+listdevicegroups
+listdevices
+listusersofdevicegroup
+listevents
+logintokens
+serverinfo
+userinfo
+adduser
+removeuser
+adddevicegroup
+removedevicegroup
+editdevicegroup
+broadcast
+showevents
+addusertodevicegroup
+removeuserfromdevicegroup
+addusertodevice
+removeuserfromdevice
+sendinviteemail
+generateinvitelink
+config
+movetodevicegroup
+deviceinfo
+editdevice
+addusergroup
+listusergroups
+removeusergroup
+runcommand
+shell
+upload
+download
+deviceopenurl
+devicemessage
+devicetoast
+addtousergroup
+removefromusergroup
+removeallusersfromusergroup
+devicesharing
+devicepower
+indexagenterrorlog
+agentdownload
+report
+
+

You can get this list by just running MeshCtrl without any argument and can get more information on each action by typing “meshctrl help [action]”

+

example

+
+

Note

+

Note that when using Intel AMT only (no agent) you can do wake (on) and power off and reset from the group action. MeshCentral should automatically using Intel AMT to perform these actions when you select "Wake-up devices", "Power off devices" or "Reset devices".

+
+

Gathering information

+

The following commands are really easy to use: serverinfo, userinfo, listusers, listdevices, listdevicegroups. They just request information from the server. Note that for these commands, you can optionally use “--json" to receive the response in JSON format. For example, getting the list of users will look like this:

+
node meshctrl.js listusers
+id, name, email
+---------------
+"admin", "admin", "username@domain.com"
+"joe", "joe", "joe@domain.com"
+"mytestuser", "MyTestUser", "a@a.com"
+"test.user", "test.user", "test.user@user.com"
+
+

In the --json argument is used, it looks like this:

+
node meshctrl.js listusers --json
+  {
+    "_id": "user//admin",
+    "name": "admin",
+    "creation": 1417814230,
+    "siteadmin": 4294967295,
+    …
+
+

The JSON version is much long and contain much more information. This format can be useful if you take the output of MeshCtrl and dump it into a file for later parsing. One really useful command is “listdevices” that just displays all the devices in the account grouped by device groups. For example:

+
node meshctrl listdevices
+
+Device group: "Lab Computers"
+id, name, icon, conn, pwr, ip
+-----------------------------
+p3HOhDapgT@VyO$upGJYxEa$v4YCY76Y2G@hOGmJnbPXjkSHP@AgJ1M6FkqSEUqg, "raspberrypi", 5, 1, 1
+yjbMXlQBf09TSIqKlkwrRucm767TcXfNbSinQWXgpdBBY5MEU1gg0kzshwiwFCOp, "tinkerboard", 5, 1, 1
+DRvCLkYIgk744tqqMr9Xvy5TK8aXkLoOXUQETnFdFepVQojyFV5gaBi5Gh4f6B6d, "LattePanda", 5, 1, 1
+ggifepc5wqK7sCVnOIjOZy9i9kaJizalIarz7Qwe5bJ4icpLD69zWYpjAaU@sfY$, "MeshLabTop", 1, 5, 1
+ECAI7NO893JoN3ntK7@mbniyDq0qriG82wqGKQF4s8SpXs3NdnvuHR76Bzq14Pik, "MeshLabRight", 1, 1, 1
+fCLFeHaxQ$T6mgICdVkCdkifiU8LNJdU73YknmxfAb@0jBF2BrhTsEIBwgpoCNx$, "DevLinux", 1, 1, 1
+hfbJ7zAgwZK@LQfsZkr1cqTSp6mjjZ3MjGC$v4X8E7HM1cZEnlGBgcorELu1hZWe, "AmtMachine11", 1, 1, 1
+YRGm4AQVRR38Ypisuo40KhvBGhDl2pE5YCp4j4eIbLaX3kmH3tmumOUbxb44A@Rh, "CoffeeTable", 1, 1, 1
+PpMJiPxtjRjfoEal$9RHdm5s31BaqDSbGc3329s49rzcXcVuTDvm4VO0YllO5XR7, "DevBox", 1, 0, 0
+tyR7l2j5@wOjDeRbOQNfjU7xB$ss6VZQPDkFsALPzJ4zbTI4IamV$OdwHeqiXV0K, "MeshLabLeft", 1, 5, 1
+i@BNTAHB5NMtDyrHMiCaz3GzYlJUUQn7qZZfh@N6271DWAM3EH6ujRNPc2snGXYX, "raspberrypi", 5, 1, 1
+2E$CjXw2Aldh3DGAzSNo5qTSgEhd4OTWcO9KGBi9ja4EOxEUHq8J1135Y2IvxOlX, "AmtMachine7", 1, 5, 1
+0Ab3O@4fgHjwVOpC0qaARfURTtKCa@QjxWPDpT5WQ0Wz5s4OvRWAgeoGT9j8k5JF, "RaspberryPi", 5, 1, 1
+
+Device group: "IntelAMT only"
+id, name, icon, conn, pwr, ip
+-----------------------------
+LN8qsH634RGjDwATIOd3ICzfKrB@t@ERow8AEyDndyAs3yqwfrfhEaMPABMg53cg, "AmtMachine7", 0, 0, 0
+Ea3GcF$EoMnDEc9Tbz$Vu9wnmTziqqcOZ0URSdYeuVn4LU9LLMT@91P5s1WLSgVA, "DevBox", 0, 0, 0
+
+

It’s also possible to list only the display the list of devices for a single group. Just add --id followed by the group identifier. You can find the group identifier using the “DeisplayDeviceGroups” command. For example:

+
node meshctrl listdevices --id 7b4b43cdad850135f36ab31124b52e47c167fba055…
+
+id, name, icon, conn, pwr, ip
+-----------------------------
+p3HOhDapgT@VyO$upGJYxEa$v4YCY76Y2G@hOGmJnbPXjkSHP@AgJ1M6FkqSEUqg, "raspberrypi", 5, 1, 1
+yjbMXlQBf09TSIqKlkwrRucm767TcXfNbSinQWXgpdBBY5MEU1gg0kzshwiwFCOp, "tinkerboard", 5, 1, 1
+DRvCLkYIgk744tqqMr9Xvy5TK8aXkLoOXUQETnFdFepVQojyFV5gaBi5Gh4f6B6d, "LattePanda", 5, 1, 1
+ggifepc5wqK7sCVnOIjOZy9i9kaJizalIarz7Qwe5bJ4icpLD69zWYpjAaU@sfY$, "MeshLabTop", 1, 5, 1
+ECAI7NO893JoN3ntK7@mbniyDq0qriG82wqGKQF4s8SpXs3NdnvuHR76Bzq14Pik, "MeshLabRight", 1, 1, 1
+fCLFeHaxQ$T6mgICdVkCdkifiU8LNJdU73YknmxfAb@0jBF2BrhTsEIBwgpoCNx$, "DevLinux", 1, 1, 1
+hfbJ7zAgwZK@LQfsZkr1cqTSp6mjjZ3MjGC$v4X8E7HM1cZEnlGBgcorELu1hZWe, "AmtMachine11", 1, 1, 1
+YRGm4AQVRR38Ypisuo40KhvBGhDl2pE5YCp4j4eIbLaX3kmH3tmumOUbxb44A@Rh, "CoffeeTable", 1, 1, 1
+PpMJiPxtjRjfoEal$9RHdm5s31BaqDSbGc3329s49rzcXcVuTDvm4VO0YllO5XR7, "DevBox", 1, 0, 0
+tyR7l2j5@wOjDeRbOQNfjU7xB$ss6VZQPDkFsALPzJ4zbTI4IamV$OdwHeqiXV0K, "MeshLabLeft", 1, 5, 1
+i@BNTAHB5NMtDyrHMiCaz3GzYlJUUQn7qZZfh@N6271DWAM3EH6ujRNPc2snGXYX, "raspberrypi", 5, 1, 1
+2E$CjXw2Aldh3DGAzSNo5qTSgEhd4OTWcO9KGBi9ja4EOxEUHq8J1135Y2IvxOlX, "AmtMachine7", 1, 5, 1
+0Ab3O@4fgHjwVOpC0qaARfURTtKCa@QjxWPDpT5WQ0Wz5s4OvRWAgeoGT9j8k5JF, "RaspberryPi", 5, 1, 1
+
+

You can also add “--count" to just return the number of devices instead of displaying them. An example of this would be:

+
node meshctrl listdevices --id 7b4b43cdad850135f36ab31124b52e47c167fba055… --count
+13
+
+

Here we see that in a specific device group, there are 13 devices.

+

Adding and removing accounts

+

MeshCtrl can be used to add and remove user accounts. This is great is you want to automate MeshCentral in some situations. Normally, an administrator can go to the “My Users” tab and press “New Account…” button to create a new account.

+

+

With MeshCtrl the same process can be accomplished using the following command line:

+
node ./node_modules/meshcentral/meshctrl adduser --user SampleUser --pass SamplePassword 
+
+

This will create a basic user account with the specified “SampleUser” username and “SamplePassword” password. In addition to the basic example above, there are plenty of additional arguments that can be used. There is a list:

+
--email [email]    - New account email address.
+--emailverified    - New account email is verified.
+--resetpass        - Request password reset on next login.
+--siteadmin        - Create the account as full site administrator.
+--manageusers      - Allow this account to manage server users.
+--fileaccess       - Allow this account to store server files.
+--serverupdate     - Allow this account to update the server.
+--locked           - This account will be locked.
+--nonewgroups      - Account will not be allowed to create device groups
+--notools          - Account not see MeshCMD download links.
+
+

Instead of specifying a password, one can specify “--randompass" to have MeshCtrl generate a random password, this is typically use along with “--resetpass" and “--email" to cause the user to perform a password reset the first time a login occurs.

+

The permission arguments are used to grant server permissions to the new account. The “-- siteadmin" argument will grant this account full site administrator rights and all other permission arguments are ignored. The other permission arguments can be combined.

+

To remove a user account, use the following command:

+
node ./node_modules/meshcentral/meshctrl removeuser --userid SampleId
+
+

Note that when creating a new user, the username is passed in. However, when removing a user account, the userid is used. One can get the list of userid’s by using the “ListUsers” command.

+

MeshCtrl will return “ok” if the command is successful and the account will be added in real-time to the “My Users” tab on the website.

+

Creating and removing device groups

+

MeshCtrl can be used to add and remove device groups. When a group is created, it’s added in the account that MeshCtrl is logged into and that account gets full rights over that device group. Typically, a new device group is created using the “Add Device Group” link in the main website.

+

+

A group has a name, type and optionally a description text. To add a device group using MeshCtrl, we use the “AddDeviceGroup” command like this:

+
node ./node_modules/meshcentral/meshctrl adddevicegroup --name "Sample group name" --desc "Sample description"
+
+

Here we specify the name and description of the new device group. This will create a normal device group that requires agents to be installed on each computer. You can also create an agent-less Intel AMT device group by adding the "--amtonly" argument. Once done, MeshCtrl will return “ok" and the group will be created in the logged in account in real-time.

+

You can delete a group by using the “RemoveDeviceGroup” command like this:

+
node ./node_modules/meshcentral/meshctrl removedevicegroup --id 7b4b43cd…dc89fe0e581c
+
+

To remove a device group, the group identifier needs to be specified. You can get that identifier using the "ListDeviceGroups" command. For device groups, the group identifier is a long hex value.

+

Adding and removing users from device groups

+

Once you created user account and device groups, the next important operation is to connect the two and grant users access to some device groups. In the web interface, this is typically done by clicking on a device group and managing the users in that page.

+

+

To do this, you first have to get the user and group identifiers. You can get these using the “listusers” and “listdevicegroups” commands. Then, you can for example do this:

+
node ./node_modules/meshcentral/meshctrl addusertodevicegroup --id 7b4b43cd…dc89fe0e581c --userid bob --fullrights
+
+

Typically, the group identifier is a long hex value. The command will add user “bob” to a given group with full rights. You can also add a user to a group with only some rights, the list is as follows:

+
  --fullrights           - Allow full rights over this device group.
+  --editgroup            - Allow the user to edit group information.
+  --manageusers          - Allow the user to add/remove users.
+  --managedevices        - Allow the user to edit device information.
+  --remotecontrol        - Allow device remote control operations.
+  --agentconsole         - Allow agent console operations.
+  --serverfiles          - Allow access to group server files.
+  --wakedevices          - Allow device wake operation.
+  --notes                - Allow editing of device notes.
+  --desktopviewonly      - Restrict user to view-only remote desktop.
+  --limiteddesktop       - Limit remote desktop keys.
+  --noterminal           - Hide the terminal tab from this user.
+  --nofiles              - Hide the files tab from this user.
+  --noamt                - Hide the Intel AMT tab from this user.
+
+

Note that if “fullrights” are granted, all other access right arguments are ignored. If successful, MeshCtrl will display “ok”, otherwise it will show an error message.

+

To remove a user from a group, use the “removeuserfromdevicegroup” command. For example:

+
node ./node_modules/meshcentral/meshctrl removeuserfromdevicegroup --id 7b4b43cd…dc89fe0e581c --userid bob
+
+

The syntax of this command is identical to the “addusertodevicegroup” command, but there are no rights arguments. MeshCtrl will also display “ok” if the command got processed correctly.

+

Message Broadcast

+

One fun command the MeshCtrl offers is the “broadcast” command. It sends a short notification message that all connected users will see in real-time. For example, you can do this:

+
node ./node\_modules/meshcentral/meshctrl broadcast --msg "This is a test" 
+
+

All connected users will see this:

+

+

The broadcast command is great for notifying users of upcoming server downtime or other events.

+

Conclusion

+

MeshCtrl is an essential tools in every MeshCentral administrator’s tool box. It allows administrators to automate various MeshCentral management tasks which can be useful for large scale management of many devices and users.

+

License

+

MeshCtrl and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/meshrouter/images/Aspose.Words.954ea56c-626b-416a-8b2a-efde10ebd3a9.002.png b/meshrouter/images/Aspose.Words.954ea56c-626b-416a-8b2a-efde10ebd3a9.002.png new file mode 100644 index 00000000..082d815a Binary files /dev/null and b/meshrouter/images/Aspose.Words.954ea56c-626b-416a-8b2a-efde10ebd3a9.002.png differ diff --git a/meshrouter/images/custom_apps1.png b/meshrouter/images/custom_apps1.png new file mode 100644 index 00000000..ea9e3719 Binary files /dev/null and b/meshrouter/images/custom_apps1.png differ diff --git a/meshrouter/images/custom_apps2.png b/meshrouter/images/custom_apps2.png new file mode 100644 index 00000000..f3609c89 Binary files /dev/null and b/meshrouter/images/custom_apps2.png differ diff --git a/meshrouter/images/custom_apps3.png b/meshrouter/images/custom_apps3.png new file mode 100644 index 00000000..0605c9f7 Binary files /dev/null and b/meshrouter/images/custom_apps3.png differ diff --git a/meshrouter/images/download-link.png b/meshrouter/images/download-link.png new file mode 100644 index 00000000..08ee692b Binary files /dev/null and b/meshrouter/images/download-link.png differ diff --git a/meshrouter/images/login.png b/meshrouter/images/login.png new file mode 100644 index 00000000..cd060264 Binary files /dev/null and b/meshrouter/images/login.png differ diff --git a/meshrouter/images/port_maps.png b/meshrouter/images/port_maps.png new file mode 100644 index 00000000..973afe16 Binary files /dev/null and b/meshrouter/images/port_maps.png differ diff --git a/meshrouter/images/port_maps2.png b/meshrouter/images/port_maps2.png new file mode 100644 index 00000000..e2ee7935 Binary files /dev/null and b/meshrouter/images/port_maps2.png differ diff --git a/meshrouter/images/port_maps3.png b/meshrouter/images/port_maps3.png new file mode 100644 index 00000000..64957677 Binary files /dev/null and b/meshrouter/images/port_maps3.png differ diff --git a/meshrouter/index.html b/meshrouter/index.html new file mode 100644 index 00000000..2ddc83cc --- /dev/null +++ b/meshrouter/index.html @@ -0,0 +1,1859 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MeshCentral Router - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

MeshCentral Router

+

+

MeshCentral Router Guide as .pdf as .odt

+

Video Walkthru

+
+ +
+ +

Abstract

+

This document takes a look at MeshCentral Router, a Windows application that performs TCP and UDP port mapping from a local machine to any remote computer thru a MeshCentral servers. This document should allow the user to Internet relay traffic thru NAT routers and firewalls.

+

Introduction

+

MeshCentral is a remote management web site that connects users to remote computers over a local network or the internet. In addition to remote management features, MeshCentral is capable of relaying TCP and UDP traffic thru the server and thru mesh agents installed on remote computers. This feature can be useful to, for example, perform a remote desktop session using RDP or route custom traffic that would otherwise be difficult to route.

+

Downloading

+

MeshCentral router is a Windows application that comes built-into the MeshCentral server or can +be downloaded at: https://meshcentral.com/tools/MeshCentralRouter.exe

+

It’s probably best to use the MeshCentral router that comes with your version of the MeshCentral +server as the two will likely be most compatible. A given MeshCentral Router version may not +work with any MeshCentral server versions. On MeshCentral, you can download MeshCentral +Router with this link:

+

Download

+

The router link will only show up on Windows browsers.

+

Login

+

MeshCentral router will need to login to your MeshCentral server just like any browser. You can +do this by entering the server name and account username and password. Depending on your +server and account situation, you may see some or all of the following screens.

+

Login

+

If the second factor authentication is required, MeshCentral Router does not support hardware +keys (WebAuthn), but does support the YubiKey™ OTP.

+

Creating Port Maps

+

Once logged in, you can start adding port maps using the “Add Map…” and “Add Relay Map…” +buttons on the bottom right. You can then create a map and open the associated application +using the “Open…” button and remote to port map using the “Remove” button.

+

port_maps

+

There are two different types of ports mappings. A normal port map and a relay port map. A +normal port map will route packets to the selected destination computer that is running the mesh +agent as shown here.

+

port_maps

+

A relay port map will route traffic thru the server and thru the remote agent to a target IP address +on the remote agent’s network as shown here.

+

+

Note that all traffic is encrypted using TLS from MeshCentral Router to the MeshCentral server +and from the server to the MeshAgent. The server and the agent do have access the traffic so it’s +recommended to use port mappings to tunnel data that is also encrypted for that end-to-end +encryption is assured.

+

Custom Apps

+

Starting v1.0.21

+

1

+

2

+

3

+

Command Line Arguments

+

MeshCentral router can be run with command line arguments to make it quicker and easier to +use. The arguments range from debugging to being able to quickly login and setting up port +maps.

+
-debug
+
+

Causes MeshCentral Router to generate a “debug.log” dump file that can be useful for +debugging.

+
-host:<hostname>
+-user:<username>
+-pass:<password>
+-ignorecert
+
+

This set of command line arguments make logging into the MeshCentral server easier. Note that +specifying the password using a command line argument may not be secure as the command +shell can record the password in the command history. The “ignorecert” argument is not +recommended as it’s going to cause MeshCentral Router to ignore untrusted server certificates. +This should only be used for debugging.

+
-map:<protocol>:<localport>:<computername>:<app>:<remoteport>
+
+

The “map” argument will automatically create a network map once MeshCentral Router is logged +In. The protocol must be “TCP” or “UDP, the local port can be 0 for any. The computer name is +the server-side name of the computer, if many computers have the same one, one of them will be +selected. The app can be empty for a custom application, or can be “HTTP”, “HTTPS”, “RDP”, +“PuTTY” or “WinSCP”. For the UDP protocol, no apps are currently supported so it should be left +blank. For example you can use:

+
-map:TCP:0:"MyComputer":HTTP:80
+-map:UDP:1235:"MyComputer"::1234
+
+

The first example will map a random local port to port 80 of “MyComputer” and is expected for +use with HTTP traffic. The second example maps local UDP port 1235 to port 1234 on +“MyComputer”. It’s best for the computer name to be in quotes.

+

In addition to port mapping, you can also setup relay maps where a remote computer is used as a +traffic relay like this:

+
-relaymap:<protocol>:<localport>:<computername>
+ :<app>:<remoteip>:<remoteport>
+
+

This will relays a local port to thru the server and thru a remote agent to a target IP address and +port. For example:

+
-relaymap:TCP:555:"MyComputer":HTTP:192.168.1.1:80
+
+

This will relay local port 555 to a 192.168.1.1:80 for HTTP traffic. A typical use of this is to be able +to remotely configure a home router from anywhere on the Internet.

+
-all
+
+

The “all” switch will bind local ports to all network interfaces so that other computers on the +network can use the port maps provided by MeshCentral Router. By default, local ports will be +bound to the loopback interface so that only local application can use the port mappings.

+
-tray
+
+

The “tray” switch will place MeshCentral Router on the Windows system tray instead of the +normal application bar.

+

MeshCentral Router with SAML or OAuth

+

You can't use the MeshCentral Router (MCR) login dialog box to login to a server that uses SAML or OAuth. However, you can still use MCR

+
    +
  1. Download MCR and run it.
  2. +
  3. In the first dialog box, there will be an "Install..." button to setup MeshCentral router with the "mcrouter://" protocol in your system registry.
  4. +
  5. Once done, close MCR.
  6. +
  7. Now log into the web UI of your MeshCentral server. Go in the bottom of the "My Devices" tab, hit the "Router" link and hit "Launch MeshCentral Router".
  8. +
  9. This will launch the router and connect directly to your server using a login cookie.
  10. +
+
+

Note

+

The only drawback is you will have to manually load mappings saved in an .mcrouter file. When not using SAML, you can click on the .mcrouter file to load the mappings and MCR at the same time.

+
+

Conclusion

+

MeshCentral Router is a free, open source tool for routing TCP and UDP traffic over the internet. It’s a powerful as it allows for any traffic to go thru NAT routers and firewalls while being encrypted using TLS.

+

License

+

MeshCentral, MeshCentral Router and this document are all opens source and licensed using +Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/messaging/images/MC2-CallMeBot3.png b/messaging/images/MC2-CallMeBot3.png new file mode 100644 index 00000000..e1bcbcd7 Binary files /dev/null and b/messaging/images/MC2-CallMeBot3.png differ diff --git a/messaging/images/MC2-Discord1.png b/messaging/images/MC2-Discord1.png new file mode 100644 index 00000000..072c3bff Binary files /dev/null and b/messaging/images/MC2-Discord1.png differ diff --git a/messaging/images/MC2-Ntfy1.png b/messaging/images/MC2-Ntfy1.png new file mode 100644 index 00000000..bbd043cd Binary files /dev/null and b/messaging/images/MC2-Ntfy1.png differ diff --git a/messaging/images/MC2-Pushover1.png b/messaging/images/MC2-Pushover1.png new file mode 100644 index 00000000..eb543dbb Binary files /dev/null and b/messaging/images/MC2-Pushover1.png differ diff --git a/messaging/images/MC2-Pushover2.png b/messaging/images/MC2-Pushover2.png new file mode 100644 index 00000000..2175c371 Binary files /dev/null and b/messaging/images/MC2-Pushover2.png differ diff --git a/messaging/images/MC2-Telegram1.png b/messaging/images/MC2-Telegram1.png new file mode 100644 index 00000000..664aa2ef Binary files /dev/null and b/messaging/images/MC2-Telegram1.png differ diff --git a/messaging/images/MC2-Telegram2.png b/messaging/images/MC2-Telegram2.png new file mode 100644 index 00000000..f8046886 Binary files /dev/null and b/messaging/images/MC2-Telegram2.png differ diff --git a/messaging/images/MC2-Telegram3.png b/messaging/images/MC2-Telegram3.png new file mode 100644 index 00000000..0a623a32 Binary files /dev/null and b/messaging/images/MC2-Telegram3.png differ diff --git a/messaging/images/MC2-Telegram4.png b/messaging/images/MC2-Telegram4.png new file mode 100644 index 00000000..3c8727fe Binary files /dev/null and b/messaging/images/MC2-Telegram4.png differ diff --git a/messaging/images/MC2-Telegram5.png b/messaging/images/MC2-Telegram5.png new file mode 100644 index 00000000..830f9c20 Binary files /dev/null and b/messaging/images/MC2-Telegram5.png differ diff --git a/messaging/index.html b/messaging/index.html new file mode 100644 index 00000000..c125b905 --- /dev/null +++ b/messaging/index.html @@ -0,0 +1,1986 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Messaging - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Messaging

+

Table of Contents

+

Introduction
+Telegram Setup
+Discord Setup
+XMPP Setup
+CallMeBot Setup
+Pushover Setup
+ntfy Setup
+Zulip Setup
+User Setup
+Administrator Management
+Two-Factor Authentication

+

Introduction

+

MeshCentral supports messaging platforms so that users can register a messaging account with MeshCentral and receive notifications. This is useful since messages are sent to an application the user is confirtable with and many messaging platforms have clients on mobile phones, desktop and more so that the notification can show up where the user is.

+

Telegram Setup

+

For Telegram integration, you will need to provide MeshCentral with the necessary login information so that MeshCentral can authenticate and connect to the Telegram servers and start sending notifications. For Telegram, both user and bot login is supported with bot login being the more typical way to go. The configuration in the config.json for a bot login looks like this:

+
{
+  "settings": {
+    "Cert": "devbox.mesh.meshcentral.com",
+  },
+  "domains": {
+    "": {
+      "title": "My Server"
+    }
+  },
+  "messaging": {
+    "telegram": {
+      "apiid": 0,
+      "apihash": "00000000000000000000000",
+      "bottoken": "00000000:aaaaaaaaaaaaaaaaaaaaaaaa"
+    }
+  }
+}
+
+

Note the "messaging" section in the config.json. For Telegram user login, it looks like this:

+
{
+  "messaging": {
+    "telegram": {
+      "apiid": 0,
+      "apihash": "00000000000000000000000",
+      "session": "aaaaaaaaaaaaaaaaaaaaaaa"
+    }
+  }
+}
+
+

User login makes use of "session", while bot login uses "bottoken". One way to get started with the setup is to run node node_modules/meshcentral --setuptelegram and follow the instructions.

+

+

In the first step, you will get the apiid and apihash values. In the second step you get the bottoken or enter your phone number and code to get the session value. Once done, when running the server manually from the command line, the server should indicate that it can connect to Telegram like this:

+
MeshCentral HTTP redirection server running on port 80.
+MeshCentral v1.0.87, Hybrid (LAN + WAN) mode.
+MeshCentral Intel(R) AMT server running on central.mesh.meshcentral.com:4433.
+MeshCentral HTTPS server running on central.mesh.meshcentral.com:443.
+MeshCentral HTTPS relay server running on relay1.mesh.meshcentral.com:443.
+MeshCentral Telegram client is bot connected.
+
+

Note the last line, indicating it's connected as a bot. If you wish to use Telegram with a proxy, here are the possible Telegram settings. You can use the proxy settings for both user or bot login modes.

+
{
+  "messaging": {
+    "telegram": {
+      "apiid": 0,
+      "apihash": "00000000000000000000000",
+      "session": "aaaaaaaaaaaaaaaaaaaaaaa",
+      "useWSS": false,                                 // Important. Most proxies cannot use SSL.
+      "proxy": {
+        "ip": "123.123.123.123",                       // Proxy host (IP or hostname)
+        "port": 123,                                   // Proxy port
+        "MTProxy": false,                              // Whether it's an MTProxy or a normal Socks one
+        "secret": "00000000000000000000000000000000",  // If used MTProxy then you need to provide a secret (or zeros).
+        "socksType": 5,                                // If used Socks you can choose 4 or 5.
+        "timeout": 2                                   // Timeout (in seconds) for connection,
+      }
+    }
+  }
+}
+
+

Discord Setup

+

For Discord integration, you need to provide MeshCentral with a bot application token so that MeshCentral can login and send notifications to users. The Discord bot will need to be joined to one or more Discord servers and users will need to join at at least one Discord server that is in common with the bot to receive notifications.

+

There are many tutorials online on how to create a Discord bot and get the login token. For example follow the two first sections of this tutorial. The "How to Create a Discord Bot Account" section will show how to create a bot and get the token, the "How to Invite Your Bot to Join a Server" section shows how to join the bot to a Discord server.

+

Note that Privleged Gateway Intents permissions is needed for the bot on Discord. If not set, MeshCentral will show an error when trying to connect to Discord.

+

+

Discord integration requires that MeshCentral be run on NodeJS v17 or higher. Once you have the Discord bot login token, the config.json Discord configuration looks like this:

+
{
+  "messaging": {
+    "discord": {
+      "serverurl": "https://discord.gg/xxxxxxxxx",
+      "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+    }
+  }
+}
+
+

Once users will need to join the same Discord server as the bot, the optional "serverurl" can be used to give the users a URL link to join the server, this can be a server invitation link or some other URL with instructions.

+

XMPP Setup

+

For XMPP integration, you need to provide MeshCentral with a XMPP server, username and password so that MeshCentral can login and send notifications to users. You can get a XMPP account to any number of servers or start up your own XMPP server.

+
{
+  "messaging": {
+    "xmpp": {
+      "service": "xmppserver.com",
+      "credentials": {
+        "username": "username",
+        "password": "password"
+      }
+    }
+  }
+}
+
+

An easy way to get setup with XMPP is to create a free account with chatterboxtown.us and then, setup MeshCentral with the service value set to "chatterboxtown.us" along with the username and password of you account. This can be done in minutes. Once setup, users will be able to setup and verify XMLL accounts and use this for notifications and 2FA verification.

+

CallMeBot Setup

+

+

CallMeBot is a free system that allows users to receive notifications on Signal Messenger, Whatsapp and Facebook Messenger. Enabling this feature is very simple, just enable it like this:

+
{
+  "messaging": {
+    "callmebot": true
+  }
+}
+
+

Once enabled, users see the new "CallMeBot" option when trying to enable messaging in MeshCentral. They will need to follow specific instructions to enable CallMeBot to send notifications to their messaging application.

+ +

Once the user has enabled their account, they can cut & paste the CallMeBot URI into MeshCentral to validate their account.

+

Pushover Setup

+

Pushover is another notification service that makes it's own mobile application. To get started, download the Pushover application and create an account then go to the https://pushover.net/ web site and setup a new application like this:

+

+

You can setup an application with a name and icon, then, once you get a application token you can add it to the config.json like this:

+
{
+  "messaging": {
+    "pushover": {
+      "token": "xxxxxxxxxxxxxxxxxxxxx"
+    }
+  }
+}
+
+

Once setup, the server will offer users the "Pushover" option when setting up messaging. Users will need to copy the Pushover user key into the dialog box to verify notifications are correct.

+

+

ntfy setup

+

ntfy is a completely free notification service. You can enable the ntfy integration with the following config.json section:

+

{
+  "messaging": {
+    "ntfy": true
+  }
+}
+
+No setup is reqired to enable this. When using ntfy, make sure you use a suffisently random topic name so that others can't guess the name and subscribe to it to receive your messages.

+

For self-hosting your own ntfy server with ACL support

+

You can set host to the DNS name of your server, userurl to the url to provide to users to setup access to your server and authorization to the Basic base64 User+Pass authenttication for your server

+
{
+  "messaging": {
+    "ntfy": {
+      "host": "myntfyserver.com",
+      "userurl": "https://myntfyserver.com/userhelp",
+      "authorization": "Basic cGhpbDpteXBhc3M="
+    }
+  }
+}
+
+

+

Zulip setup

+

You can enable the MeshCentral Zulip integration with the following config.json section:

+
{
+  "messaging": {
+    "zulip": {
+      "site": "https://api.zulip.com",
+      "email": "your-bot@zulip.com",
+      "api_key": "your_32_character_api_key"
+    }
+  }
+}
+
+

Slack setup

+

Slack integration is achieved by the use of Incoming Webhooks. +You can get started by following the Slack guide here and getting your URL

+

Once you have your incoming webhooks url, You can enable the Slack integration with the following config.json section

+
{
+  "messaging": {
+    "slack": true
+  }
+}
+
+

User Setup

+

Once one or more messaging systems are setup with MeshCentral, users will be able to register their handle and verify that they own that account by typing in a 6 digit code.

+

+

This verification is necessary so that MeshCentral does not send notifications to incorrect messaging accounts.

+

Administrator Management

+

When users setup a messaging account, a messaging bubble will show up next to their name in the "My Users" tab. You can also click on a user to see and edit it's messaging handle and message them. Currently MeshCentral can only send messages, no receive.

+

+

Two-Factor Authentication

+

By default, messaging is used as a second factor for login when a user enabled a messaging account. Users will need to messaging icon on the login screen and can opt to receive a 6 digit code to login.

+

+

As an administrator you can turn off use of messaging for 2FA using the following settings in the config.json:

+
{
+  "settings": {
+    "Cert": "devbox.mesh.meshcentral.com",
+  },
+  "domains": {
+    "": {
+      "title": "My Server",
+      "passwordRequirements": {
+        "msg2factor": false
+      }
+    }
+  }
+}
+
+

Notice the msg2factor is set to false. In this case, messaging can still be used for user notifications, but will not be offered as a 2FA option.

+

For administrators, login reports will show if "Messaging" was used as a second factor for a user login. You can see this in this report:

+

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/other/adfs_sso_guide/index.html b/other/adfs_sso_guide/index.html new file mode 100644 index 00000000..4f2426cb --- /dev/null +++ b/other/adfs_sso_guide/index.html @@ -0,0 +1,1670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADFS SSO Guide - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

ADFS SSO Guide

+

Assumptions

+

The following guide was built under the assumptions that:

+
    +
  1. ADFS 4.0 running on Server 2016 using Active Directory
  2. +
  3. Main ADFS setup already completed / working. SSL certs installed and port forwarded as expected.
  4. +
+

The guide was built to deal specifically with adding mesh as a Relying Party. I’m far from an ADFS expert and some configurations may not be needed. Most of this was built by reading the code and taking guesses as to the needed values.

+

Guide

+

As with anything SSO, you need 2 pieces – the IDP setup (in this case ADFS) and the SP setup (in this case Mesh).

+

Mesh Setup

+

Add the following to your mesh config file in the domains part:

+

+

Callback URL: Should be the FQDN for your Mesh Server, ending with /auth-saml-callback

+

Entity ID: This is how ADFS IDs which party the request goes to. You can set this to whatever you want, but you will need this value later on when working in ADFS.

+

IDP URL: This is the URL to ADFS. Ends with /adfs/ls unless you did something very weird in ADFS.

+

Cert: You will need to export the token signing cert from ADFS, then convert it to PEM format. This cert can be found in ADFS -> Service -> Certificates. You can use this openssl command to convert it from CRT to PEM format:

+
openssl x509 -in mycert.crt -out mycert.pem -outform PEM
+
+

Save the config and restart the mesh server.

+

Windows Server Configuration

+

ADFS setup (in pictures):

+

Relying Party Trust -> New Relying Party Trust

+

+

+

+

+

+

+

+

+

+

Edit the new “Relying Party Trust” Properties:

+

+

+

Then it’s time to add Claims…

+

+

+

+

For the outgoing claim type, where it says firstname, lastname, and email, manually type it in as shown. All other fields should be selected from the dropdown.

+

Add another rule:

+

+

+

For all fields, select from the dropdowns

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/other/images/adfs_sso2022-05-16-23-24-54.png b/other/images/adfs_sso2022-05-16-23-24-54.png new file mode 100644 index 00000000..55b434b6 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-24-54.png differ diff --git a/other/images/adfs_sso2022-05-16-23-25-48.png b/other/images/adfs_sso2022-05-16-23-25-48.png new file mode 100644 index 00000000..7300847c Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-25-48.png differ diff --git a/other/images/adfs_sso2022-05-16-23-26-01.png b/other/images/adfs_sso2022-05-16-23-26-01.png new file mode 100644 index 00000000..1e2917cb Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-26-01.png differ diff --git a/other/images/adfs_sso2022-05-16-23-26-47.png b/other/images/adfs_sso2022-05-16-23-26-47.png new file mode 100644 index 00000000..d0429080 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-26-47.png differ diff --git a/other/images/adfs_sso2022-05-16-23-26-56.png b/other/images/adfs_sso2022-05-16-23-26-56.png new file mode 100644 index 00000000..822a0dc0 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-26-56.png differ diff --git a/other/images/adfs_sso2022-05-16-23-27-03.png b/other/images/adfs_sso2022-05-16-23-27-03.png new file mode 100644 index 00000000..745ecfc0 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-27-03.png differ diff --git a/other/images/adfs_sso2022-05-16-23-27-09.png b/other/images/adfs_sso2022-05-16-23-27-09.png new file mode 100644 index 00000000..90b7a09a Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-27-09.png differ diff --git a/other/images/adfs_sso2022-05-16-23-27-16.png b/other/images/adfs_sso2022-05-16-23-27-16.png new file mode 100644 index 00000000..385f72a8 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-27-16.png differ diff --git a/other/images/adfs_sso2022-05-16-23-27-22.png b/other/images/adfs_sso2022-05-16-23-27-22.png new file mode 100644 index 00000000..e0c5615d Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-27-22.png differ diff --git a/other/images/adfs_sso2022-05-16-23-27-28.png b/other/images/adfs_sso2022-05-16-23-27-28.png new file mode 100644 index 00000000..d85957a5 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-27-28.png differ diff --git a/other/images/adfs_sso2022-05-16-23-27-41.png b/other/images/adfs_sso2022-05-16-23-27-41.png new file mode 100644 index 00000000..ef104675 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-27-41.png differ diff --git a/other/images/adfs_sso2022-05-16-23-27-51.png b/other/images/adfs_sso2022-05-16-23-27-51.png new file mode 100644 index 00000000..63b36028 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-27-51.png differ diff --git a/other/images/adfs_sso2022-05-16-23-28-09.png b/other/images/adfs_sso2022-05-16-23-28-09.png new file mode 100644 index 00000000..87cfd7b3 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-28-09.png differ diff --git a/other/images/adfs_sso2022-05-16-23-28-15.png b/other/images/adfs_sso2022-05-16-23-28-15.png new file mode 100644 index 00000000..15c0cc66 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-28-15.png differ diff --git a/other/images/adfs_sso2022-05-16-23-28-21.png b/other/images/adfs_sso2022-05-16-23-28-21.png new file mode 100644 index 00000000..7ee9e9aa Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-28-21.png differ diff --git a/other/images/adfs_sso2022-05-16-23-28-41.png b/other/images/adfs_sso2022-05-16-23-28-41.png new file mode 100644 index 00000000..87cfd7b3 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-28-41.png differ diff --git a/other/images/adfs_sso2022-05-16-23-28-48.png b/other/images/adfs_sso2022-05-16-23-28-48.png new file mode 100644 index 00000000..77e13157 Binary files /dev/null and b/other/images/adfs_sso2022-05-16-23-28-48.png differ diff --git a/other/images/sat2022-05-17-00-58-46.png b/other/images/sat2022-05-17-00-58-46.png new file mode 100644 index 00000000..c08fc565 Binary files /dev/null and b/other/images/sat2022-05-17-00-58-46.png differ diff --git a/other/images/sat2022-05-17-00-59-21.png b/other/images/sat2022-05-17-00-59-21.png new file mode 100644 index 00000000..fd35c4f4 Binary files /dev/null and b/other/images/sat2022-05-17-00-59-21.png differ diff --git a/other/images/sat2022-05-17-01-00-22.png b/other/images/sat2022-05-17-01-00-22.png new file mode 100644 index 00000000..394d9241 Binary files /dev/null and b/other/images/sat2022-05-17-01-00-22.png differ diff --git a/other/images/sat2022-05-17-01-00-33.png b/other/images/sat2022-05-17-01-00-33.png new file mode 100644 index 00000000..fcb41d12 Binary files /dev/null and b/other/images/sat2022-05-17-01-00-33.png differ diff --git a/other/images/sat2022-05-17-01-00-45.png b/other/images/sat2022-05-17-01-00-45.png new file mode 100644 index 00000000..ede4e6fc Binary files /dev/null and b/other/images/sat2022-05-17-01-00-45.png differ diff --git a/other/images/sat2022-05-17-01-00-59.png b/other/images/sat2022-05-17-01-00-59.png new file mode 100644 index 00000000..d9a43bc3 Binary files /dev/null and b/other/images/sat2022-05-17-01-00-59.png differ diff --git a/other/images/sat2022-05-17-01-01-13.png b/other/images/sat2022-05-17-01-01-13.png new file mode 100644 index 00000000..f8fca5a8 Binary files /dev/null and b/other/images/sat2022-05-17-01-01-13.png differ diff --git a/other/images/sat2022-05-17-01-01-52.png b/other/images/sat2022-05-17-01-01-52.png new file mode 100644 index 00000000..12e0e414 Binary files /dev/null and b/other/images/sat2022-05-17-01-01-52.png differ diff --git a/other/images/sat2022-05-17-01-02-04.png b/other/images/sat2022-05-17-01-02-04.png new file mode 100644 index 00000000..a821a71d Binary files /dev/null and b/other/images/sat2022-05-17-01-02-04.png differ diff --git a/other/images/sat2022-05-17-01-02-10.png b/other/images/sat2022-05-17-01-02-10.png new file mode 100644 index 00000000..771fac24 Binary files /dev/null and b/other/images/sat2022-05-17-01-02-10.png differ diff --git a/other/images/sat2022-05-17-01-02-39.png b/other/images/sat2022-05-17-01-02-39.png new file mode 100644 index 00000000..f5154f34 Binary files /dev/null and b/other/images/sat2022-05-17-01-02-39.png differ diff --git a/other/images/sat2022-05-17-01-03-30.png b/other/images/sat2022-05-17-01-03-30.png new file mode 100644 index 00000000..d6b4febd Binary files /dev/null and b/other/images/sat2022-05-17-01-03-30.png differ diff --git a/other/images/sat2022-05-17-01-03-43.png b/other/images/sat2022-05-17-01-03-43.png new file mode 100644 index 00000000..bd12d4c5 Binary files /dev/null and b/other/images/sat2022-05-17-01-03-43.png differ diff --git a/other/images/sat2022-05-17-01-03-56.png b/other/images/sat2022-05-17-01-03-56.png new file mode 100644 index 00000000..3732d666 Binary files /dev/null and b/other/images/sat2022-05-17-01-03-56.png differ diff --git a/other/images/sat2022-05-17-01-04-11.png b/other/images/sat2022-05-17-01-04-11.png new file mode 100644 index 00000000..70056e76 Binary files /dev/null and b/other/images/sat2022-05-17-01-04-11.png differ diff --git a/other/images/sat2022-05-17-01-04-22.png b/other/images/sat2022-05-17-01-04-22.png new file mode 100644 index 00000000..5c2216a6 Binary files /dev/null and b/other/images/sat2022-05-17-01-04-22.png differ diff --git a/other/images/sat2022-05-17-01-05-15.png b/other/images/sat2022-05-17-01-05-15.png new file mode 100644 index 00000000..59f14d14 Binary files /dev/null and b/other/images/sat2022-05-17-01-05-15.png differ diff --git a/other/images/sat2022-05-17-01-05-22.png b/other/images/sat2022-05-17-01-05-22.png new file mode 100644 index 00000000..f821334e Binary files /dev/null and b/other/images/sat2022-05-17-01-05-22.png differ diff --git a/other/images/sat2022-05-17-01-05-28.png b/other/images/sat2022-05-17-01-05-28.png new file mode 100644 index 00000000..44e63822 Binary files /dev/null and b/other/images/sat2022-05-17-01-05-28.png differ diff --git a/other/images/sat2022-05-17-01-05-51.png b/other/images/sat2022-05-17-01-05-51.png new file mode 100644 index 00000000..5427a075 Binary files /dev/null and b/other/images/sat2022-05-17-01-05-51.png differ diff --git a/other/images/sat2022-05-17-01-06-01.png b/other/images/sat2022-05-17-01-06-01.png new file mode 100644 index 00000000..035a74d8 Binary files /dev/null and b/other/images/sat2022-05-17-01-06-01.png differ diff --git a/other/images/sat2022-05-17-01-06-10.png b/other/images/sat2022-05-17-01-06-10.png new file mode 100644 index 00000000..e0e62c02 Binary files /dev/null and b/other/images/sat2022-05-17-01-06-10.png differ diff --git a/other/meshcentral_satellite/index.html b/other/meshcentral_satellite/index.html new file mode 100644 index 00000000..992e7527 --- /dev/null +++ b/other/meshcentral_satellite/index.html @@ -0,0 +1,1788 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + MeshCentral Satellite - MeshCentral Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + +

MeshCentral Satellite

+

Coming Soon

+

+

Abstract

+

MeshCentral Satellite is a Windows application that acts as a relay between a Windows Active Directory Domain and a MeshCentral Server. Once setup, the MeshCentral server can request that MeshCentral Satellite create an 802.1x computer profile for Intel AMT in the domain, or ask a certificate authority to issue or revoke a certificate.

+

Introduction

+

A MeshCentral server can run in the cloud on a Linux server, but it may occasionally need to interact with a domain controller to perform some operations. MeshCentral Satellite is built to perform this function. MeshCentral Satellite is a Windows application built in C# and must run on a computer that is part of a domain and must run with sufficient rights to perform LDAP object addition and removal. If a certificate authority (CA) needs to be used, MeshCentral Satellite needs to have sufficient rights to ask the CA issue or revoke certificates.

+

MeshCentral Satellite should run on a computer that is always on. Once running, it will attempt to connect to the MeshCentral server and keep the connection open awaiting requests.

+

+

Currently, MeshCentral Satellite can perform four operations on behalf of the MeshCentral server:

+
    +
  • Create an Intel AMT domain computer.
  • +
  • Remove an Intel AMT domain computer.
  • +
  • Issue a certificate for Intel AMT.
  • +
  • Revoke an Intel AMT certificate.
  • +
+

MeshCentral Satellite can run both as a standalone application which is practical to get started and it can be setup as a background Windows Service for long term operations. To get started, we will run it as a standalone application and start working on getting it setup.

+

Installation and Configuration

+

Start by creating an empty folder on a computer that is part of the domain you need to interact with. For example, create a “c:\MeshCentralSatellite” then copy “MeshCentralSatellite.exe” into that folder. You can find that executable in the “node_modules/meshcentral/agents” folder of your server. If it’s not present, update your server to the latest version and look again.

+

Once started you should see something this:

+

+

Go in the file menu and select “Settings…”, this is where all of the MeshCentral Satellite setting are. It includes sections to connect to your MeshCentral server. Security groups you want device to join and Certificate settings.

+

+

First, enter your MeshCentral server hostname and username and password. MeshCentral Satellite can’t use two-factor authentication (2FA) so you need to create a user account in MeshCentral for Satellite or you can create a login token in the “My Account” section of MeshCentral with an unlimited expire time and cut & paste the username and password given into MeshCentral Satellite.

+

+

If your MeshCentral server is a test server that does not have a real TLS certificate, you can opt to ignore the TLS certificate on connection but this is not secure and so, not recommended.

+

+

The “Device Name” has two options, “Operating System Name” or “Node Identifier”. If you opt for “Operating System Name”, Intel AMT devices will show as, for example, “iME-ComputerName” in the list of domain computers. If “node identifier” is used, Intel AMT devices will look like “iME-xxxxxxxxxxx” where xxx is the start of the MeshCentral node identifier for this device.

+

Using the node identifier is more secure as it can’t easily be replicate by any other device. The operating system name would be impersonated by another device causing various security issues.

+

The security groups section will list any security groups created until the “Computers” section of the domain controller. Checking one or more of these security groups will automatically going new Intel AMT devices to these groups.

+

+

Lastly, we have the certificate authority and certificate settings. If a certificate authority needs to used, enter the name of the CA which is in the format “\” you can then hit the check box next to the name and select the certificate template to use.

+

+

For certificate configuration, you can leave it as-is with “SAM Account Name” and the common name and all alternative names selected.

+

Once done, you can hit ok. The settings will be saved in a file called “config.txt” in plain text in the same folder as “MeshCentralSatellite.exe”. Make sure not to grant access to this file to anyone not authorized to do so as it will have the MeshCentral login username and password.

+

Once done, select “Local Connect” the “Files” menu to connect to the MeshCentral server.

+

+

+

MeshCentral Satellite should be connected and ready to receive commands from the server.

+

Checking the connection

+

In order to make sure the server correctly recognizes the MeshCentral Satellite connection, you can go to the server console and type “usersession”. The Satellite session should be marked.

+

+

Currently, you should only have a single satellite session per user. In the future, multiple sessions could be supported for redundancy.

+

Configuring Intel® AMT 802.1x

+

To start using MeshCentral Satellite, you can configure Intel AMT with an 802.1x profile, this is done in the domain section of the config.json. Here is an example of an 802.1x EAP-TLS profile that will require that Intel AMT be issued a certificate:

+
      "AmtManager": {
+        "802.1x": {
+          "AuthenticationProtocol": "EAP-TLS",
+          "SatelliteCredentials": "admin"
+        },
+        "WifiProfiles": [
+          {
+            "ssid": "Network-8021x",
+            "authentication": "wpa2-802.1x",
+            "encryption": "ccmp-aes"
+          }
+        ]
+      }
+
+

In this following example, MSCHAPv2 is used and so, MeshCentral Satellite will need to generate a random password, save it in the active directory and send the password back to MeshCentral for Intel AMT configuration:

+
      "AmtManager": {
+        "802.1x": {
+          "AuthenticationProtocol": "PEAPv0/EAP-MSCHAPv2",
+          "SatelliteCredentials": "admin"
+        },
+        "WifiProfiles": [
+          {
+            "ssid": "Network-8021x",
+            "authentication": "wpa2-802.1x",
+            "encryption": "ccmp-aes"
+          }
+        ]
+      }
+
+

The second example does not require that a certificate authority be setup, the first example does. In both cases, the WIFI profile is set to “wpa2-802.1x” and so, the 802.1x profile will be setup for both the Intel AMT wired interface and the specified WIFI profile for wireless.

+

Note that is both examples, “SatelliteCredentials” indicates the account name that MeshCentral Satellite will be connected on. In our case, we used the “admin” account that matches the account configuration we used in sections 3 and 4.

+

Make these changes to the config.json and restart the MeshCentral server. Once done, any device groups that are set to configure Intel AMT will generate operations for MeshCentral Satellite.

+

Computer and certificate operations

+

Once MeshCentral and MeshCentral Satellite are setup, make sure a device group has an active Intel AMT policy. In the example below, we have a device group with an Admin Control Mode (ACM) activation policy.

+

+

Computers connecting to this device group will automatically be setup with the new 802.1x and WIFI profile, but you can go in an agent console and type “amtconfig” to force the check of the Intel AMT configuration. In our case, it looks like this:

+

+

MeshCentral is adding a new WIFI profile, setting up 802.1x and issuing a new Intel AMT certificate from the domain CA. This was all done in a few seconds. On the MeshCentral Satellite side, we see this:

+

+

The MeshCentral Satellite received an 802.1x EAP-TLS request. It asked Intel AMT to generate a RSA key pair, to sign a certificate request, forwarded the request to the domain CA for signature and finally returned the final certificate to Intel AMT. The computer account in the domain was also updated and looks like this:

+

+

The new Intel AMT device was added to the domain along with the Intel AMT version and node identifier in the description. If “Node Identifier” was selected as the computer name in MeshCentral Satellite settings, the friendly name would be in the description and the node identifier would be used as the device name.

+

Finally, it’s worth taking a look at how Intel AMT was configured before and after this operation. Before setting up the 802.1x profile, Intel AMT looked like this:

+

+

+

Note that there is no 802.1x profiles or WIFI profiles. After the new configuration, MeshCommander shows Intel AMT looking like this:

+

+

+

There are now two new certificates in the “Security” tab. One if the root of the domain CA, the other is the certificate assigned to the Intel AMT device by the CA. You also see the WIFI 802.1x profile. In this example, the device did not have a wired network interface, but if it did, 802.1x would also be setup for the wired interface.

+

Running as a Background Service

+

MeshCentral Satellite can be run as a background service. This is useful when running for lang periods on a domain server. You can use the “Service” menu in MeshCentral Satellite to install, start, stop and uninstall the Windows service. Make sure to come and run “MeshCentralSatellite.exe” from the correct location you want to install the Windows Service from.

+

+

Since MeshCentral Satellite need to have domain rights to add and remove computer objects from the active directory and to have certificate authority (CA) rights, you may want to install the service, go in the service manager and change the “Log On” account to one with the proper rights.

+

+

Once set, you can start the service from within MeshCentral Satellite. Once the service is started, there is a communication channel that will be create with the local application so you can still monitor what the service is doing.

+

+

The lines starting with “Service:“ are coming from the background service. At this point, you can close the local application and the service will keep running in the background.

+

License

+

MeshCentral, MeshCentral Satellite and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 00000000..1aedb749 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"MeshCentral Documentation","text":""},{"location":"#about","title":"About","text":"

MeshCentral is a full computer management web site. With MeshCentral, you can run your own web server to remotely manage and control computers on a local network or anywhere on the internet. Once you get the server started, create device group and download and install an agent on each computer you want to manage. A minute later, the new computer will show up on the web site and you can take control of it. MeshCentral includes full web-based remote desktop, terminal and file management capability.

For more information, visit MeshCentral.com.

"},{"location":"#social-media","title":"Social Media","text":"

YouTube

Reddit

Twitter

BlogSpot

"},{"location":"#documentation","title":"Documentation","text":"

The User's Guide contains information every administrator should know including usage, the server configuration file, databases, TLS offloading, Lets Encrypt, IP Filtering, Email setup, embedding, server port aliasing, reverse proxy setup, multi factor authentication, branding & terms of use, HashiCorp Vault support, and SSO.

The Installation Guide has detailed instructions for installing the MeshCentral Server on Windows 8.1, Windows 10, Windows 2012 R2, Amazon Linux 2, Raspberry Pi, Microsoft Azure, Google Cloud, Ubuntu 18, Ubuntu 16 and OpenBSD.

The Design and Architecture Guide is a short document that includes information on the design overview, dependencies, source code descriptions of each file, certificates, TLS security, the agent to server handshake, browser to agent relay and WebRTC and the messenger service.

"},{"location":"#video-tutorials","title":"Video Tutorials","text":"

You can watch many tutorial videos on the MeshCentral YouTube Channel. Two videos to get started involve installation and basic usages.

Installing MeshCentral on Windows, Linux and macOS. MeshCentral - Installation

Basic Usages including installing the agent and remote desktop, terminal and file access. MeshCentral - Basics

MeshCentral support for two-factor authentication. MeshCentral - Two Factor Authentication

How to setup MeshCentral with the NGINX reverse proxy. MeshCentral - NGINX Reverse Proxy

Installing and using the MeshCentral Android agent. MeshCentral - Android

Using MeshCentral Router to port map TCP connections. MeshCentral - Basics

"},{"location":"#feedback","title":"Feedback","text":"

If you encounter a problem or have a suggestion to improve the product, you may file an issue report

If you are filing a problem report, you should include:

  • The version of the software you are using
  • The Operating System and version
  • The observed output
  • The expected output
  • Any troubleshooting you took to resolve the issue yourself
  • Any other similar reports~~

If you are having issues with the following other products, you should file a report on their respective issue pages MeshAgent MeshRouter License

This software is licensed under Apache 2.0.

"},{"location":"design/","title":"Design and Architecture","text":"

Design and Architecture Guide as .pdf as .odt

"},{"location":"design/#video-walkthru","title":"Video Walkthru","text":""},{"location":"design/#abstract","title":"Abstract","text":"

This document attempts to describe the architecture and design of the second version of MeshCentral on which work started in late 2016. The document covers the overview of the design, goes in details about the protocol and various decisions and trade-offs. This document is intended for anyone that wants to understand the inner workings of MeshCentral or someone that wants to make a security review of the software. The software and added documentation and tutorial videos are available at: https://www.meshcommander.com/meshcentral2

"},{"location":"design/#introduction","title":"Introduction","text":"

MeshCentral is a free open source web-based remote computer management software. After over 8 years of working on the first version of MeshCentral, work as moved to version 2 which this document described. In 2010, when MeshCentral v1 was first designed, the Internet was very different. HTML5 and WebSocket did not exists, no such thing as a software container, etc. With MeshCentral version 2, a complete redesign was made to make the software much more in line with modern Internet deployment models.

The advent of NodeJS, WebSocket, WebRTC and other web technologies coming out in the last 10 years has really made the design of MeshCentral v2 not only possible, but quite amazing. Being able to use a single programming language across platforms to JavaScript. Being able to easily exchange objects using web socket and JSON, being able to scale with WebRTC, deploy quickly with containers, etc. Looking back at the incredible advances in web technologies lead to an almost mandatory MeshCentral redesign.

"},{"location":"design/#goals-requirements","title":"Goals & Requirements","text":"

The goal of MeshCentral is to be the best open source remote management software in the world. Remote computer management is a big area with many different usages and requirements. To best suite this, it\u2019s important to have software that is as flexible as possible. Additionally, there are many other goals:

  • Must be quick and easy to install.
  • Must install on all major operating systems and platforms.
  • Can be deployed on small computers and the cloud.
  • Can be deployed within containers.
  • Can be deployed in many network environments.
  • Must support both software agent and Intel\u00ae AMT hardware agent.
  • Must only use open source dependencies.
  • Must provide all basic remote management features (desktop, terminal, files\u2026)
  • Must use the network efficiently.
  • Must have a real time user interface.
  • Must be easy to use.
  • Must be fast.
  • Etc.

Basically, all the requirements you would expect from open source software that can go viral. Since this software is sponsored by Intel, it\u2019s going to support Intel\u00ae AMT really well, making it possible to manage a remote computer regardless of its OS or power state. Intel\u00ae AMT is not required to use this software, however it\u2019s a great fit.

"},{"location":"design/#design-overview","title":"Design Overview","text":"

In this section, we do a very high level overview of MeshCentral\u2019s design. MeshCentral has 3 big components: the server, the agent and the web application.

There is of course more software that support these 3 components like the Windows Server Installer, ClickOnce application, MeshCentral Discovery Tool and more. These will be covered later. Most of the document will focus on these 3 main components. Another component that is significant but not part of the software itself is Intel\u00ae AMT (Intel\u00ae Active Management Technology). MeshCentral supports Intel AMT that acts like an optional hardware based agent for MeshCentral.

When it comes to programming languages used, MeshCentral is mostly built with JavaScript with the agent having significant portable C code. This makes things pretty simple since the browser, server and agents can share some of the code. More importantly, JavaScript is great at parsing JSON and so, the main protocol used between the components is JSON over Web Socket.

It\u2019s important to note that while JavaScript is used in all 3 components, the JavaScript runtime is very different. The JavaScript written to run within a browser sandbox uses different calls than the one running in NodeJS on the server or on the agent with DukTape.

This is probably a good time to introduce DukTape (https://www.duktape.org/). Unlike the browser and NodeJS JavaScript engines, DukTape is a less known JavaScript runtime written in C. The agent is built in C code with little smarts other than being able to securely connect back to the server. The server then pushes to the agent a JavaScript file that the agent runs. This makes the agent very flexible since a developers can quickly change the JavaScript that is pushed to the agent and change the agent\u2019s behavior instantly.

Another interesting design decision is that MeshCentral makes almost no use of RESTful API\u2019s. Instead, almost everything is done using WebSocket. This allows JSON objects to be exchanged fully asynchronously. There is no pushing the refresh button or polling as events are sent by all actors in real time.

"},{"location":"design/#meshcentral-server","title":"MeshCentral server","text":"

The MeshCentral server is a NodeJS application that is published on NPM at: https://www.npmjs.com/package/meshcentral Many administrators can get started quickly using \u201cnpm install meshcentral\u201d once NodeJS is installed. MeshCentral will work on Node 6.x and higher.

"},{"location":"design/#dependencies","title":"Dependencies","text":"

The server makes use of the following dependencies on NPM. These are all automatically installed by NPM when installing MeshCentral.

Can be found in the file: MeshCentralServer.njsproj

The main takeaway is that MeshCentral is mostly an ExpressJS application. This is not a complete list of dependencies as many of these packages have their own dependencies creating a large tree. The security of these packages is a concern and all of the dependency tree is a concern. In addition to the dependencies that are \u201chard coded\u201d, there are a few more that are installed only when needed. These are:

"},{"location":"design/#node-windows","title":"node-windows","text":"

greenlock, le-store-certbot, le-challenge-fs: Installed on all Windows install. Allows background service install:

le-acme-core: Installed only when Let\u2019s Encrypt must be used:

mongojs: Installed when MongoDB is in used.

nodemailer: Installed when SMTP server support is in used.

MeshCentral will run npm install automatically when any of these optional modules are needed but not currently available.

"},{"location":"design/#understanding-the-different-modes-lan-wan-and-hybrid","title":"Understanding the different modes: LAN, WAN and Hybrid","text":""},{"location":"design/#code-files-and-folders","title":"Code files and folders","text":"

Someone would think the server is rather simple when taking a look at the MeshCentral server code files. At a high level, the entire server has 3 folders, 3 text files and a manageable number of .js files that are fairly self-descriptive. Here is a list of the source files and folders.

"},{"location":"design/#folders","title":"Folders","text":"

agents: Compiled agents, install scripts, tools and agent JavaScript.

public: Static web elements such as images, CSS, HTML and more.

views: Main web application, login screen and messenger app.

"},{"location":"design/#configuration-text-files","title":"Configuration & text files","text":"

package.json: Description of the MeshCentral package on NPM. sample-config.json: A sample \u201cconfig.json\u201d file to get started. readme.txt: Readme file published with the MeshCentral package.

"},{"location":"design/#code-files","title":"Code files","text":"
 amtevents.js             | Used to decode Intel AMT WSMAN events.\n amtmanager.js            | Used to handle Intel AMT/CIRA things.\n amtprovisioningserver.js | Used to Provision Intel AMT on a Local Network.\n amtscanner.js            | Used to scan a local network for Intel AMT machines.    \n amtscript.js             | Used to run Intel AMT scripts from MeshCommander.       \n certoperations.js        | Used to generate and perform certificate operations.    \n common.js                | Various commonly used methods.\n crowdsec.js              | Used to handle all crowdsec security features                     \n db.js                    | Used to access the MongoDB or NeDB database.            \n exeHandler.js            | Used to modify windows executables.\n firebase.js              | Used to handle Google Firebase things.\n interceptor.js           | Used to insert credentials in an HTTP stream.           \n letsencrypt.js           | Used to obtain and use a Let\u2019s Encrypt certificate.\n mcrec.js                 | Standalone Session Recording Indexer.\n meshaccelerator.js       | Used to offload RSA sign to other CPU cores.            \n meshagent.js             | Used to communicate to agents.\n meshbot.js               | Sample bot to connect to meshcentral and preform various tasks                     \n meshcentral.js           | The is the main module, gets the server started.\n meshctrl.js              | MeshCtrl performs command line actions on a MeshCentral server.\n meshcore.js              | Main Agent Code that runs on your remote devices.\n meshdesktopmultiplex.js  | Used to handle remote desktop multiplexing.\n meshdevicefile.js        | Used to handle file download requests.\n meshipkvm.js             | Used to handle IP KVM integration\n meshmail.js              | Used to send SMTP mails.\n meshmessaging.js         | Used to handle all users messaging methods like 2FA.\n meshrelay.js             | Used to relay agent and browser web socket connections. \n meshscanner.js           | MeshCentral server discovery when in LAN mode.\n meshsms.js               | Used to handle all users sms methods.\n meshuser.js              | Used to communicate with browsers.                      \n mpsserver.js             | Used to communicate to Intel\u00ae AMT CIRA.\n mqttbroker.js            | Used to create/handle an MQTT broker (beta)               \n multiserver.js           | Used for server-to-server communication.                \n pass.js                  | Performs password hash + salt.                          \n redirserver.js           | Used to handle HTTP traffic.                            \n swarmserver.js           | Used to upgrade legacy MeshCentralv1 agents. \n webauthn.js              | Handles all WebAuthN things.\n webrelayserver.js        | Used for all HTTP/HTTPS web relaying from agents.          \n webserver.js             | Handles HTTPS traffic.                                  \n winservice.js            | Server background install on Windows.\n

At a high level, the MeshCentral.js file will get the server started. By default, it will start the webserver.js on port 443, redirectserver.js on port 80 and mpssrver.js on port 4433. The webserver.js file will create a meshuser.js or meshagent.js instance each time a user or agent connects. The other files support various usages, but this is the basic working on the server.

"},{"location":"design/#server-database","title":"Server database","text":"

One of the big design decision on the server is its database. We want something that scales and so, opted to deal with the good and the bad of a NoSQL database, MongoDB. On the other hand, we want the server to be really simple to install for people who want to try it out or want to manage 100 computers or less. We don\u2019t want the added learning curve of MongoDB for people that don\u2019t really need it. It turned out, we can have both. NeDB is a NPM package that provides a simple MongoDB-like API while being completely implemented in NodeJS. For most people, this is plenty good to get started.

By default, MeshCentral will just create and use a NeDB database, but can be configured to use MongoDB. The internal code path for both databases are almost exactly identical so the \u201cdb.js\u201d file handles both, almost the same way and the exact database in use is completely abstracted from the rest of the server code.

"},{"location":"design/#certificates","title":"Certificates","text":"

MeshCentral makes use of many certificates to accomplish many security tasks. When first running the server or an agent, both of these actors will generate certificates. The agent will generate one or two certificates on the first run and the server will generate four certificates.

In this section we review what certificates are created, what are their uses and how they are stored. Most administrators using MeshCentral will not need a deep understanding of this section to run the server, however, a basic understanding of this section can help understand how to best protect the server\u2019s critical security assets.

"},{"location":"design/#server-certificates","title":"Server Certificates","text":"

As indicated above, the MeshCentral server creates four certificates when it first runs. It uses ForgeJS to perform certificate creation and all four certificates below are saved in the \u201cmeshcentral-data\u201d folder. The four certificates are:

"},{"location":"design/#server-root","title":"Server root","text":"

root-cert-public.crt

This is a self-signed root certificate that is used only to issue the 3 next certificates. This certificate can be useful when it\u2019s installed as a root of trust in some situations. For example, when Intel AMT connects to the MPS server on port 4433, it will correctly connect only if this root certificate is loaded into Intel AMT as a trusted certificate. Browser can also be setup to trust this root certificate in order to create a trusted connection between a browser and the servers HTTPS port. This certificate is RSA3072 unless the option \u201c--fastcert\" is used, in that case a RSA2048 certificate is generated.

"},{"location":"design/#mps-certificate","title":"MPS certificate","text":"

mpsserver-cert-public.crt

This is a TLS certificate signed by the root above used as a TLS server certificate on the MPS port 4433. Intel AMT computers will connect to this port and verify the certificate time, common name and that it\u2019s signed by the root above. This certificate is not typically changed, even when the server is running in production. This certificate is always generated as RSA2048 because older Intel AMT firmware will not accept certificates with larger keys.

"},{"location":"design/#web-certificate","title":"Web certificate","text":"

webserver-cert-public.crt

This is the default certificate used to secure the HTTPS port 443. It is signed by the root above and is the certificate users will first see then connecting the browser to the server. Often, users will need to ignore the browser security warning. This certificate is RSA3072 unless the option \u201c--fastcert\" is used, in that case a RSA2048 certificate is generated. In production environments, this certificate is replaced with a real certificate. There are many ways to change this certificate for a more appropriate certificate in production environments:

  • You can replace the \u201cwebserver-cert-*\u201d files in the \u201cmeshcentral-data\u201d folder.
  • You can use Let\u2019s Encrypt which will override this certificate automatically.
  • You can use a reverse-proxy in front of the server with \u201c--tlsoffload\".
"},{"location":"design/#agent-certificate","title":"Agent certificate","text":"

agentserver-cert-public.crt

This certificate is used to authenticate the server to agents. It\u2019s signed by the root above and when installing an agent, the hash of this certificate is given to the agent so that it can connect back to the server securely. This certificate is RSA3072 unless the option \u201c--fastcert\" is used, in that case a RSA2048 certificate is generated.

The \u201cmeshcentral-data\u201d folder contains critical server information including private keys therefore, it\u2019s important that it be well protected. It\u2019s important to backup the \u201cmeshcentral-data\u201d folder and keep the backup in a secure place. If, for example the \u201cagent certificate\u201d on the server is lost, there is no hope for agents ever be able to connect back to this server. All agents will need to be re-installed with a new trusted certificate.

If someone re-installs a server, placing the \u201cmeshcentral-data\u201d folder back with these certificates should allow the server to resume normal operations and accept connections for Intel AMT and agents as before.

"},{"location":"design/#agent-certificates","title":"Agent Certificates","text":"

The mesh agent generates one or two RSA certificates when it first starts. On smaller IoT devices such as a Raspberry Pi, this can take a little while to do and the CPU will spike to 100% during this time. This is normal and only occurs the first time the agent runs.

The certificates are generated a little differently depending on the platform. On Windows, the Mesh Agent will use Microsoft cryptographic providers to harder the agent root cert. If available, the agent will use the platform TPM to harden the certificate. On other platforms, only one certificate is generated and used for both agent authentication to the server and WebRTC session authentication.

"},{"location":"design/#agent-root-certificate","title":"Agent root certificate","text":"

This certificate is the root trust of the agent. The SHA384 hash of this certificates public key is the agent\u2019s identifier on the server. When a agent connects to the server using web socket, it performs a secondary authentication check using this certificate. The server will compute the agent\u2019s identifier after the agent sent a signed proof to the server. This certificate is also used to sign the secondary certificate below when it\u2019s needed.

"},{"location":"design/#secondary-certificate","title":"Secondary certificate","text":"

This is a certificate signed by the agent root above. It\u2019s currently only used by WebRTC to perform dTLS authentication to a remote browser. This certificate does not need to be signed by a trusted CA for WebRTC purposes since the hash of the certificate will be sent to the browser using a trusted path. If the agent root certificate is not hardened using platform cryptography, the secondary certificate is not created and the agent root cert is used for all purposes.

A possible attack would occur if someone were to be able to access the agent root certificate. They could impersonate the agent to the server. Agents don\u2019t have any rights to perform management operations on the server or other agents, but by impersonating a agent, a rogue agent would pretend to be an office computer to which administrator would login with their username & password, especially when the root is not hardened. Some care should be taken to protect the \u201cmeshagent.db\u201d file and to not give important information to untrusted agents.

"},{"location":"design/#tls-security","title":"TLS Security","text":"

MeshCentral makes heavy use of Transport Layer Security (TLS) and datagram-TLS (dTLS) to authenticate and encrypt network traffic between the browser, server and agent. Configuring TLS settings correctly is essential to making sure communications are secure and to minimize attacks on open ports.

Probably the most important TLS configuration is for the MeshCentral server ports 443 and 4433. These two ports are exposed to the Internet and so, should be setup as securely as possible.

"},{"location":"design/#meshcentral-https-port-443","title":"MeshCentral HTTPS port 443","text":"

The HTTPS port on the MeshCentral server will only support TLS 1.2 and above, and makes use of only 6 cypher suites:

TLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_256\\_GCM\\_SHA384 (0xc030) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_256\\_CBC\\_SHA384 (0xc028) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_256\\_CBC\\_SHA (0xc014) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_128\\_GCM\\_SHA256 (0xc02f) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_128\\_CBC\\_SHA256 (0xc027) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_128\\_CBC\\_SHA (0xc013) \n

Note that these cipher suites are all perfect forward secrecy (PFS) suites and are considered cryptographically secure as of the writing of this document. When the server is deployed on the Internet, https://ssllabs.com gives the server an A rating with no known vulnerabilities and no weak ciphers detected.

SSL Labs confirms that all major browsers should be able to connect correctly to this server.

"},{"location":"design/#meshcentral-mps-port-4433","title":"MeshCentral MPS port 4433","text":"

The Manageability Presence Server (MPS) port 4433 is used for incoming Intel AMT CIRA connections. By default it uses a TLS certificate that is signed by a self-signed root certificates. This port is not intended to be connected to by typical browsers, only Intel AMT should connect to this port. Note that the TLS certificate generated by MeshCentral for port 4433 is RSA 2048bits, this is because older Intel AMT firmware don\u2019t support RSA 3072. Because the port is not secured using a trusted certificate, SSL Labs will not rate the security of this server.

This is fully expected. Note that SSL Labs will not test servers that are not on port 443. To perform a test like this MeshCentral must be set temporarily with the MPS port set to 443 and the normal HTTPS port set to a different value.

Because older Intel AMT computers that only support TLS 1.0 are common, the server supports TLS v1.0, v1.1 and v1.2 with the following 12 cipher suites:

TLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_256\\_GCM\\_SHA384 (0xc030) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_256\\_CBC\\_SHA384 (0xc028) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_256\\_CBC\\_SHA (0xc014) \nTLS\\_RSA\\_WITH\\_AES\\_256\\_GCM\\_SHA384 (0x9d) \nTLS\\_RSA\\_WITH\\_AES\\_256\\_CBC\\_SHA256 (0x3d) \nTLS\\_RSA\\_WITH\\_AES\\_256\\_CBC\\_SHA (0x35) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_128\\_GCM\\_SHA256 (0xc02f) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_128\\_CBC\\_SHA256 (0xc027) \nTLS\\_ECDHE\\_RSA\\_WITH\\_AES\\_128\\_CBC\\_SHA (0xc013) \nTLS\\_RSA\\_WITH\\_AES\\_128\\_GCM\\_SHA256 (0x9c) \nTLS\\_RSA\\_WITH\\_AES\\_128\\_CBC\\_SHA256 (0x3c) \nTLS\\_RSA\\_WITH\\_AES\\_128\\_CBC\\_SHA (0x2f)  \n

The suites starting with \u201cTLS_RSA_\u201d don\u2019t have perfect forward secrecy (PFS) and so, are considered weak by SSL Labs. However, these are generally the suites that are supported by Intel AMT.

"},{"location":"design/#agent-to-server-handshake","title":"Agent to server handshake","text":"

One interesting aspect of MeshCentral\u2019s design is how the agent connects to the server. We wanted a way for the agent to connect to the server that would be similar to how browsers connect to web servers. This allows for a large number of agents to connect just like if a large number of browsers where connecting. All of the infrastructure that helps web server\u2019s scale would be put to use in the same way for agent connections. For example: TLS offload hardware, load balancers, reverse-proxies, web server scaling, etc. could all be put to use. It also makes the server easier to setup because only one port (HTTPS 443) is needed for both users and agents.

One big difference between the agent connecting and a typical browser is how the server is authenticated. Browsers have a set of known trusted root certificates. The server\u2019s web certificate is checked for validity including the name, time trusted CA and more. The agent does not have this. Instead, it just has a hash to a private server certificate.

The public facing web certificate of the server can change frequently. For example, Let\u2019s Encrypt certificates are valid 90 days. Agents need to be able to validate a specific server for a long time and don\u2019t really need to trust anything else except one specific server. We also don\u2019t want to tie the agents to a specific domain name as we could change that in the future or want to support servers with dynamic IP addresses and no fixed DNS names.

To handle all this, the agent performs a TLS connection to the server and will first see the web certificate of the server. It will then exchange a set of web socket binary messages to the server to perform a secondary authentication with the server.

The secondary check allows the agent to confirm that this server does own the private key of the private certificate expected by the agent. The agent caches the hash of the \u201couter\u201d web certificate. When re-connecting, if the agent sees the same outer web certificate, it will skip the secondary check. For obvious security raisons, it\u2019s important that the agent not accept any management messages until the secondary check is completed or skipped.

To prevent man-in-the-middle attacks, the secondary check also \u201cpins\u201d the outer web certificate. That is, the server both confirms it\u2019s the right server and indicates to the agent the hash of the outer certificate that it must have seen during the TLS connection. The agent must check this hash to make sure there is no attacker in the middle.

The agent connection design allows for reverse-proxies and TLS offload hardware. The agent will first connect a TLS session to the offload hardware. Clear traffic flows between the offload hardware and the server which will perform the secondary check if needed.

To makes all this work, the MeshCentral server must be able to fetch the hash of the outer web certificate from the reverse proxy. In this case, the server does not need the private key to the web certificate. Note that when the outer web certificate is updated, the server may have to perform many secondary checks at the same time causing a server slowdown during this time. To help with this, MeshCentral will offload the RSA signing operation to many slave processes (as many as the CPU core count on the server) to speed this up. In addition, native NodeJS RSA signing is used (not ForgeJS).

The details of the secondary certificate check look like the diagram below. To boost speed, the exchange is fully asynchronous and both sides send the first message as soon as the TLS connection completes.

Note that these messages are binary (not JSON). The agent must be able to connect to the server independently of the JavaScript that is running in DukTape. So this exchange is handled by native C code in the agent. Binary message 1 is sent immediately after the TLS connection is setup. Both sides will send binary message 2 when message 1 is received and message 3 when message 2 is received.

In addition, there are two extra messages of interest that can be sent by the agent right at the start. The agent may send the server message number 4 if the secondary check can be skipped and may send binary message number 5 indicating what server hash it expects to verify. Message number 5 is interesting because a server may have many \u201cidentities\u201d at the same time, and so, the server will use message number 5 in order to use the right Agent Server certificate.

In order to be as secure as possible, all hashes use SHA384 and certificates are RSA3072 and nonces are generated on both sides using a cryptographic random source. The server and agent signatures are computed like this:

While the server will often skip its RSA signature operation due to the agents caching the outer web certificate, the server must perform an RSA verify to each agent connection. This can\u2019t be skipped but is needed to authenticate the agent.

Once connected, the trust relationship between the server and the agent is one-way. That is, the server has management rights on the agent, but the agent does not have any right on the server. This is important since the agent does not, by default, have any credentials to the server. Any agent can connect to the server and claim to be part of a device group.

"},{"location":"design/#browser-to-agent-relay-and-webrtc","title":"Browser to agent relay and WebRTC","text":"

Browsers and agents often need to communicate to each other. Data sessions are used for desktop, terminal, file transfers, etc. and must be setup securely.

To setup a session between a browser and the agent, the server will send a URL to both sides to connect to. The URL is generated by the server and includes a unique connection token. It is sent to both the browser and agent using the web socket control channel and a JSON message. Both sides perform a websocket connection to the target URL and the server will \u201cpipe\u201d both sessions together to act as a passive relay. For security, the agent will only accept connections to the URL given by the server if the server has the same outer web certificate as its control connection. Also note that in this mode, the session is not end-to-end encrypted. The server is performing a TLS decrypt and re-encrypt and the traffic cost is high as each byte of data has to be received and sent again.

The relay server is just websocket server that will wait for connections with session tokens. When two connection with the same connection token arrive, the server makes sure that at least one of the two connections is an authenticated user, it then sends the character \u201cc\u201d on both sides to inform both parties that the relay is starting and then pipes both sessions together. Once the session is started, the browser and agent are free to send messages to each other. Note that when the server sends the relay URL to the agent, it also sends to the agent the user\u2019s permissions flags. This may be used by the agent to limit what the user can do on this session.

With this design, the flow control between the browser and agent is simple, each session gets its own end-to-end connection and the server will apply appropriate TCP back pressure on both sides as needed.

A unique feature of MeshCentral is its use of WebRTC. WebRTC was introduced in major browsers as a way to allow browsers to directly communicate to each other and perform audio/video streaming. The mesh agent has a WebRTC data-only stack that is custom built for this project in C code. It\u2019s compatible with Chrome and Firefox implementations and once a session is set up, allows data to flow directly from the browser to the agent, bypassing the server.

The use of WebRTC allows MeshCentral to scale better, to offer a faster user experience and lower hosting costs all at the same time. However, WebRTC is not easy, especially when you must maintain the C code for it and have to keep up with browser implementations, but the benefits are clear.

To setup WebRTC, browsers typically use STUN and TURN servers to get traffic thru any network obstacles (routers, proxies, firewalls). This infrastructure can be complex to setup especially if an administrator is not familiar with WebRTC concepts. To make things easy, MeshCentral opted to always start by using a websocket relay thru the server to get things started. While a session is active, the browser and agent will attempt to automatically switch the session traffic to WebRTC when possible. This way, the session always works and gets more efficient when network conditions allow.

To perform the switch-over, both browser and agent will exchange WebRTC control messages over the newly established web socket relay session.

In order to differentiate session traffic from WebRTC control traffic, the browser and agent agree to send WebRTC setup traffic using web socket text fragments. All other session traffic is sent using binary fragments. The agent has a special API allowing a session to be piped for a single fragment type. So we can perform a remote desktop session to the agent while trying to setup WebRTC at the same time.

The browser will kick off the WebRTC setup sending the initial WebRTC offer with the agent responding with a WebRTC answer. If the WebRTC session gets setup, both sides need to negotiate a clear transition from the web socket session to the WebRTC session. To do this, both sides send a start switch control fragment (this is a text fragment), the other side will respond with an ACK when the web socket session is flushed out and it\u2019s safe to switch.

On the agent side, the new WebRTC session inherits the user access rights of the web socket. Currently, the web socket channel is still maintained open. While it\u2019s not strickly needed, the web socket session terminates more cleanly than WebRTC and so, oddly its closure is used to signal the end of the WebRTC session.

"},{"location":"design/#messenger","title":"Messenger","text":"

MeshCentral includes its own messaging web application it can be used to chat, transfer files and optionally used for audio and video chat. It\u2019s used to support two different usages: User-to-user and user-to-computer communication. In the first usage, two users that are connected to the same MeshCentral server at the same time can chat. If you are a MeshCentral administrator, you can see the list of currently logged in users and hit the chat button to launch a chat invitation. If accepted, the Messenger is open on both sides and the session starts. Alternatively, while managing a remote computer, an administrator can hit the chat button to cause the remote computer to open a web browser to the chat application.

The chat app is standalone web application that is served by the MeshCentral server using a connection token and title in the URL. Once loaded in its own web frame, the messenger web application will get the connection token and title from the URL and proceed to connect to the URL using web socket. The same web socket relay that is used for browser-to-agent connections is also used in this case for browser-to-browser connections. The server relay acts the same and pipes both sessions together after sending the character \u201cc\u201d to both sides. At this point, the messenger application will show the remote user as connected and chat and file transfers can start. File transfers are just a set of binary messages sent over the web socket session with lots of JSON control messages.

Once the web socket session is setup, the messenger application will then attempt to perform a switch over to WebRTC. Both web application start by selecting a random number (not cryptographic) and the highest number will initiate the WebRTC offer. The other party will answer and both sides will trade interface candidates as they are discovered. If successful, the web socket session are flushed and the traffic is switched over to WebRTC. Because the switchover is done cleanly, it can occur while in the middle of a file transfer without the file being corrupted.

Finally, the web application will determine if the local computer is attached to a microphone and if it has a camera. If so, these options are offered in the chat window and audio/video chat is available for use. The chat app allows for one way setup of audio & video sessions. This is typically what is needed in support scenarios where the audio/video session is one-way.

The messenger web application will setup a separate WebRTC connection for audio/video in each direction but the code is present to augment the WebRTC control channel with audio/video which is a bit more efficient but more testing is needed before defaulting to this mode.

"},{"location":"design/#additional-resources","title":"Additional Resources","text":"

In addition to this document, there are a growing set of MeshCentral resources at: https://www.meshcommander.com/meshcentral2. This includes an Installer\u2019s documents, a User\u2019s Guide and plenty of YouTube tutorial videos. For developers, it\u2019s best to start on the MeshCentral GitHub repository at: https://github.com/Ylianst/MeshCentral. If any issues are found, it\u2019s best to create a new issue in GitHub or mail ylianst@gmail.com

"},{"location":"design/#conclusion","title":"Conclusion","text":"

MeshCentral is a free, open source and powerful remote management solution that is cross- platform. In this document, we have covered the goals, overview, design and some details of the software. It\u2019s hoped that this document will encourage developers to take a look at MeshCentral for more usages and review its security in detail. MeshCentral\u2019s use of modern web technologies make it a unique and amazing solution for remote management of computers. As with any good software, MeshCentral will continue to be updated and evolve.

"},{"location":"design/#license","title":"License","text":"

MeshCentral and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0

"},{"location":"how-to-contribute/","title":"Contribute to MeshCentral","text":""},{"location":"how-to-contribute/#contributing-to-meshcentral-via-github-pull-request","title":"Contributing to MeshCentral via GitHub Pull Request","text":"

If you're looking to contribute beyond translations, such as updating documentation or enhancing the software by adding features or fixing bugs, the process involves several key steps:

  1. Fork the Repository: Start by forking the MeshCentral repository on GitHub. This creates a copy of the repository under your own GitHub account, allowing you to make changes without affecting the original project.

  2. Make Your Changes

    • In your forked repository, create a new branch to keep your changes organized. This helps in managing different contributions separately.
    • Make the necessary changes in your repository. This could involve updating documentation files or modifying code to add new features or fix bugs.
  3. Review Your Changes: Before submitting your work, carefully review the changes you\u2019ve made. Check the \"Files Changed\" section on GitHub to ensure that all modifications are intended and correctly implemented.

  4. Submit a Pull Request

    • Once your changes are ready and reviewed, submit a pull request (PR) from your branch to the master branch of the main MeshCentral repository.
    • When creating the pull request, provide a clear and detailed description of what changes have been made and why. This helps maintainers understand the purpose of your contributions.
  5. Wait for Review: After submitting your pull request, wait for a project maintainer to review your contribution. Review time can vary depending on the complexity of the changes and the availability of the maintainers.

  6. Respond to Feedback: The maintainer may request further modifications or provide feedback on your pull request. Be prepared to make additional changes based on their suggestions to ensure that your contribution meets the project\u2019s standards and requirements.

  7. Final Steps: Once your pull request is approved and merged by a maintainer, your contributions will be incorporated into the MeshCentral project. Congratulations, and thank you for helping improve MeshCentral!

"},{"location":"how-to-contribute/#contribute-to-meshcentrals-multilingual-support","title":"Contribute to MeshCentral's Multilingual Support","text":"

To make MeshCentral multilingual, your contributions are crucial. Follow these steps to translate the interface into various languages.

  1. Remove Local Translations: Delete translate.json from your meshcentral-data folder. This file contains your local copy of translations, which may become outdated as new features and texts are added.

  2. Access MeshCentral: Ensure you are logged into MeshCentral.

  3. Open Translation Tool: Visit https://YOURMESHCENTRALSERVER.COM/translator.htm to access the translation interface.
  4. Choose a Language: Select the language you wish to translate from the list provided.

  5. Translate Text: Use the search function or scroll through the list to find text segments you want to translate. Utilize the \"show no translations only\" checkbox to filter untranslated texts.

  6. Enter Translations: For each text segment, enter your translation in the bottom box (not the top one) and click SET (F1).
  7. Repeat Translation: Continue translating by repeating steps 5 and 6 for other texts as desired.

  8. Save and Apply Translations

    • Click SAVE TO SERVER (F3) to save your translations to meshcentral-data/translate.json locally in your MeshCentral server.
    • Optionally, click SAVE TO FILE (F4) to download the translate.json file for offline review or sharing.
  9. Deploy Translations: Click TRANSLATE SERVER and allow some time for the process to complete (approximately 5-15 minutes depending on server specifications). This command line output will indicate when the translation is complete.

  10. Finalize Changes: It\u2019s crucial to restart MeshCentral to ensure that the translated files are picked up correctly.

  11. Share your translations: Once a language translation is complete, take the latest translation.json and share it by emailing it to the maintainer (Ylianst, ylianst@gmail.com) or by submitting it to the MeshCentral GitHub repository via a pull request.
"},{"location":"how-to-contribute/#additional-information","title":"Additional Information:","text":"
  • If you make any changes to default.handlebars, run the translate server to propagate these modifications to the language-specific handlebar files located in node_modules/meshcentral/views/translations.

By following these steps, you help MeshCentral support any language you choose, making it more accessible worldwide. By sharing your translations with us, you also help make these languages available to other users, improving the community and extending the software's reach.

"},{"location":"install/","title":"Quick Start Guide","text":""},{"location":"install/#installation","title":"Installation","text":"

Getting started is easy. If you don't have it already, install NodeJS. Then, create an empty folder and do this:

npm install meshcentral\nnode node_modules/meshcentral\n

That's it. MeshCentral will set itself up and start managing computers on your local network. By default it will be setup in LAN mode and agents you install will multicast on the local network to find the server. To setup the server so that agents use a well known DNS name and to start customizing your server, go in the \"meshcentral-data\" folder and edit the config.json file. The configuration file must be valid JSON, you can use this link to validate the file format.

For Windows users, you can download the MeshCentral Installer that will automate installation of NodeJS and provide basic configuration of the server. This option is not recommended for advanced users.

Win32 MeshCentral Installer

By default, MeshCentral will use NeDB as this is the built-in database. For more advanced users, it's recommended to switch to using MongoDB. MeshCentral can be installed on a very small server. A Raspberry Pi or AWS t3.nano running Amazon Linux 2 instance for 5$ a month will do just fine for managing up to a few hundred devices.

You can run the MeshCentral Server with --help to get options for background installation.

"},{"location":"install/#configuration","title":"Configuration","text":"

Once you get MeshCentral installed, the first user account that is created will be the server administrator. So, don't delay and navigate to the login page and create a new account. You can then start using your server right away. A lot of the fun with MeshCentral is the 100's of configuration options that are available in the config.json file. You can put your own branding on the web pages, setup a SMTP email server, SMS services and much more.

You can look here for simple config.json, here for a more advanced configuration and here for all possible configuration options. You can also take a look at the tutorial videos for additional help.

"},{"location":"install/#video-walkthru","title":"Video Walkthru","text":""},{"location":"install/install2/","title":"Full Install Guide","text":""},{"location":"install/install2/#abstract","title":"Abstract","text":"

This guide is specifically intended to help users install MeshCentral from start to finish. Once installed, you can take a look at the MeshCentral user\u2019s guide for information on how to configure MeshCentral for your specific use. In this document, we will look at installing MeshCentral on AWS Linux, Raspberry Pi and Ubuntu.

"},{"location":"install/install2/#docker","title":"Docker","text":"

https://github.com/Ylianst/MeshCentral/pkgs/container/meshcentral

docker pull ghcr.io/ylianst/meshcentral:master\n

Warning

Do not use the built in mesh update function. Update docker the docker way.

"},{"location":"install/install2/#docker-compose","title":"Docker Compose","text":"
version: '3'\nservices:\n  meshcentral:\n    restart: unless-stopped # always restart the container unless you stop it\n    image: ghcr.io/ylianst/meshcentral:1.1.27 # 1.1.27 is a version number OR use master for the master branch of bug fixes\n    ports:\n      - 80:80 # HTTP\n      - 443:443 # HTTPS\n      - 4433:4433 # AMT (Optional)\n    volumes:\n      - data:/opt/meshcentral/meshcentral-data # config.json and other important files live here\n      - user_files:/opt/meshcentral/meshcentral-files # where file uploads for users live\n      - backup:/opt/meshcentral/meshcentral-backups # location for the meshcentral backups - this should be mounted to an external storage\n      - web:/opt/meshcentral/meshcentral-web # location for site customization files\nvolumes:\n  data:\n    driver: local\n  user_files:\n    driver: local\n  backup:\n    driver: local\n  web:\n    driver: local\n
"},{"location":"install/install2/#quick-start","title":"Quick Start","text":"

For some who want to skip this document entirely, there are quick install scripts that will get a MeshCentral2 instance up and running on Linux in a few minutes. These scripts will pretty much do what this document explains very rapidly. Right now, there are two such scripts available:

"},{"location":"install/install2/#amazon-linux-2","title":"Amazon Linux 2","text":"

For Amazon EC2 users, that want to manage 100 devices or less. Launch a t3.nano or t3.micro EC2 instance with Amazon Linux 2 with TCP ports 22 (SSH), 80 (HTTP), 443 (HTTPS) and 4433 (CIRA) open. Then login as ec2-user and enter the following commands:

wget https://meshcentral.com/scripts/mc-aws-linux2.sh\nchmod 755 mc-aws-linux2.sh\n./mc-aws-linux2.sh\n

This will download the fast install script and once run, will install nodejs, meshcentral, setup systemd and start the server. For a larger instance like a t3.small, t3.medium or larger you can run the following that does the same but also installs MongoDB.

wget https://meshcentral.com/scripts/mc-aws-linux2-mongo.sh\nchmod 755 mc-aws-linux2-mongo.sh\n./mc-aws-linux2-mongo.sh\n

After these scripts are run, try accessing the server using a browser. MeshCentral will take a minute or two to create certificates after that, the server will be up. The first account to be created will be the site administrator \u2013 so don\u2019t delay and create an account right away. Once running, move on to the MeshCentral\u2019s user\u2019s guide to configure your new server.

"},{"location":"install/install2/#microsoft-azure","title":"Microsoft Azure","text":"

For 100 devices or less, launch an instance of Ubuntu 18.04 using a small B1s instance. Set the username to default in all lower case and open ports 22, 80, 443 and 3389 using the basic network profile. Then start the instance and run the following lines.

wget https://meshcentral.com/scripts/mc-azure-ubuntu1804.sh\nchmod 755 mc-azure-ubuntu1804.sh\n./mc-azure-ubuntu1804.sh\n

In this situation, port 3389 will be used to receive Intel AMT CIRA connections instead of port 4433. After these scripts are run, try accessing the server using a browser. MeshCentral will take a minute or two to create certificates after that, the server will be up. The first account to be created will be the site administrator \u2013 so don\u2019t delay and create an account right away. Once running, move on to the MeshCentral\u2019s user\u2019s guide to configure your new server.

"},{"location":"install/install2/#server-security-adding-crowdsec","title":"Server Security - Adding Crowdsec","text":"

MeshCentral has built-in support for a CrowdSec bouncer. This allows MeshCentral to get threat signals from the community and block or CAPTCHA requests coming from known bad IP addresses.

"},{"location":"install/install2/#video-walkthru","title":"Video Walkthru","text":""},{"location":"install/install2/#windows-installation","title":"Windows Installation","text":"

MeshCentral is constructed entirely with NodeJS, an asynchronous event driven JavaScript runtime (https://nodejs.org/). A basic understanding on NodeJS may be preferable but not compulsory. MeshCentral server which heavily relies on NodeJS runtime will be able run on almost any computing platform with contemporary operating systems including Windows*, Linux* and macOS*.

There are two ways to get MeshCentral setup.

  • For Linux*, macOS*, or advanced users can use CLI based NPM tool.
  • For Windows users, you can use the MeshCentral installation tool.
"},{"location":"install/install2/#windows-installation-tool","title":"Windows Installation Tool","text":"

The MeshCentral installer tool for Microsoft Windows can be downloaded at https://www.meshcommander.com/meshcentral2 or by clicking this link. This tool will automatically detect and install NodeJS if needed. NodeJS will be downloaded from https://nodejs.org checked and installed. We recommend the installer be run on a modern version of Windows (.e.g. Win8.1, Win10, Win Server 2012* or better)

During installation, the installation tool will prompt for the following settings:

  • Multi-user Server : By enabling this option, the server will be open to any user with a web browser app. Users will be able to create accounts and start managing computers associated in their respective accounts.

Note

If this option is disabled (unchecked), the server will run as a single-user server, no login screen will be presented and MeshCentral application will be limited to the server host machine only.

  • Auto-update Server: By enabling this option, the server will check new version releases daily and perform automatic update.

Note

Update check occurs at 0000 between 0100 hours (local time). During update, the server will not be accessible until update is completed.

  • Server Modes, LAN, WAN or Hybrid:

    LAN mode: Recommended for small installation within a local network. Server host does not need a fixed IP address or DNS record to operate.

    WAN or Hybrid modes: Server host will require a fixed IP address or DNS record to function correctly. If selected, user will need to enter server\u2019s DNS name or static IP address in the Server Name field. This name or IP address will be used by browsers and agents to connect back to the server, this name MUST be correct or the server will not work. If you do not have a fixed name, select LAN mode to get started.

Acquiring a static IP or DNS record is beyond the scope of this document. Please seek advice or consult your network administrator if unsure. If unsure, leave the settings as default (as-is) and proceed setup in LAN mode to manage computers that reside within the same network.

Once installed MeshCentral will run as a background Windows Service and can be accessed using a web browser with the link provided by the installer.

The installation tool can be run again to perform server update, re-installation or un-installation. When performing an update check, the tool will look at the currently installed version and compare it to the one present on NPM.

By default, MeshCentral will use TCP ports 80 (HTTP), 443 (HTTPS) and 4433 (Intel\u00ae AMT CIRA). The installer will add Windows Defender Firewall rules to allow incoming connections on these ports. In addition, if the server is in LAN or Hybrid mode, an addition rule on UDP port 16990 is added to allow for server discovery.

"},{"location":"install/install2/#npm-installation-for-advanced-users","title":"NPM Installation for Advanced Users","text":"

For advanced users or administrators, MeshCentral can be installed with NPM, a NodeJS package manager that can be accessed via web browser (https://www.npmjs.com/) or command line tool, npm.

Note

As a prerequisite, NodeJS and NPM must be installed on host OS and HTTP/HTTPS proxy settings maybe required if server host resides behind a HTTP proxy server.

  1. To begin, start a command line terminal (Windows Command Prompt or Linux Terminal) and type the following to verify if nodeJS and npm has been installed correctly as shown below a. To check on nodeJS installed version, type node \u2013v and hit enter key b. To check on npm installed version, type npm \u2013v and hit enter key

  2. If MeshCentral installation is performed on a server host that resides behind a HTTP proxy, NPM\u2019s proxy settings must be updated with respective proxy settings associated with the network environment. Skip this step if not applicable.

    .e.g. for http proxy `npm config set proxy http://proxy.com:88`\n.e.g. for https proxy `npm config set https-proxy http://proxy.com:88`\n

  3. Create a new directory MeshCentral and run the NPM install command as shown below:
    mkdir meshcentral\ncd meshcentral\nnpm install meshcentral\n
    Warning: Do not use sudo in front of npm install meshcentral.
  4. Upon download completion, the server can be started with the commands below:
    node node_modules/meshcentral [arguments]\n
    Warning: Do not run MeshCentral by going into the node_modules/meshcentral folder as this may cause auto-install and self-update features to fail. Instead, go into the directory above node_modules and run node node_modules/meshcentral. Note: If MeshCentral is started without any arguments, default settings in LAN-only mode will be in effect and user/administrator will only be able to manage computers that reside within the local network.
  5. To manage computers over the internet, the server needs to have static IP settings or a DNS record that resolves back to the right server. The mesh agents will be using the mechanism to call home to MeshCentral server. For WAN or Hybrid mode, run one of the commands below
    node node_modules/meshcentral --cert servername.domain.com\nnode node_modules/meshcentral --cert hostname.domain.com\nnode node_modules/meshcentral --cert 1.2.3.4\n
    Note: On first attempt running on WAN or Hybrid Mode:
    • Certificates will be generated for the first time and this may take a few minutes to complete.

Note

At this point, no user account will be created or available for the user hence 1st user account will be the most privileged user with Administrator rights

  • User is advised to create an admin account immediately by navigating to https://127.0.0.1 with a web browser.

Note: To run MeshCentral as a service, run it using --install argument. Once running, start a web browser and access MeshCentral application with respective URL.

"},{"location":"install/install2/#windows-defender-firewall-settings","title":"Windows Defender Firewall Settings","text":"

On Windows, the built-in firewall will need to be configured to allow TCP ports 80, 443 and 4433 and sometimes UDP port 16990. The MeshCentral Windows Installer will add incoming rules for these ports automatically. If using the advanced NPM installation or when changing the default ports, it may be needed to add or edit these firewall rules. In this section we look at how to do this.

To get started, we need to go in the control panel, click System and Security then Windows Defender Firewall and Advanced Settings on the left side then click on Inbound rules. This will get us on the right place to add or edit firewall rules.

If the MeshCentral Windows Installer was used, the MeshCentral Server TCP ports and optionally MeshCentral Server UDP ports rules should already be present.

"},{"location":"install/install2/#editing-the-existing-rules","title":"Editing the existing rules","text":"

To edit an existing rule, simply double click on it. To change the allowed inbound ports, go to the Protocols and Ports tab and change the local ports.

"},{"location":"install/install2/#add-new-firewall-rules","title":"Add new firewall rules","text":"

To add a new firewall rule, click on the New Rule\u2026 then select Port and ok. TCP or UDP and enter the specific local ports needed and ok. Then click ok twice, enter the rule name and ok again.

Typically, inbound TCP ports 80, 443 and 4433 are used, but the rule can be added with different ports as needed.

"},{"location":"install/install2/#amazon-linux-2_1","title":"Amazon Linux 2","text":"

In this section, we will look at installing MeshCentral on Amazon AWS with Amazon Linux 2. This is a low cost instance and a free tier is available so you can experiment or run a small instance of MeshCentral and it will work perfectly fine.

"},{"location":"install/install2/#getting-the-aws-instance-setup","title":"Getting the AWS instance setup","text":"

On AWS EC2, you can launch an instance and select Amazon Linux 2. In this case, it\u2019s the first option available.

When launching a new instance, you are asked to use or create a security group with the allowed inbound TCP and UDP ports. The security group should look like this:

All security group rules should have a source of 0.0.0.0/0 and ::/0. The last rule for port 8080 is only needed if migrating from a MeshCentral1 server, most people don\u2019t need it and should not be added.

If you are not going to be managing Intel AMT computers, you can remove port 4433. One can also remove port 80, however it\u2019s needed to get a Let\u2019s Encrypt certificate and useful to route users from the HTTP to the HTTPS web page.

For all the following sections, we assume that we are in the ec2-user home path. You can do:

cd ~\n

This will change the current path to the home folder.

"},{"location":"install/install2/#installing-nodejs","title":"Installing NodeJS","text":"

To get started, launch an instance and start a SSH session to it. You can use SSH on Linux or Putty on Windows to login to the AWS instance.

The first thing to do is get NodeJS installed on the instance. We will be installing a long term support (LTS) version of NodeJS. Additional information on how to do this can be found here. We first install the node version manager then activate it and install the NodeJS LTS. It\u2019s done with 3 commands:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash\n. ~/.nvm/nvm.sh\nnvm install --lts\n

We can test what version of NodeJS is installed using:

node -v\n
"},{"location":"install/install2/#installing-mongodb","title":"Installing MongoDB","text":"

If we are going to run a large instance, it\u2019s best to use MongoDB as the database. If you are using a small instance, you can skip installing MongoDB and MeshCentral will use NeDB instead which is a light weight database that is probably great for managing less than 100 computers.

If you want to use MongoDB, we can install MongoDB Community Edition. More information on how to do this can be found here.

Using nano create the file /etc/yum.repos.d/mongodb-org-4.0.repo:

sudo nano /etc/yum.repos.d/mongodb-org-4.0.repo\n

Then, put this in it:

[mongodb-org-4.0]\nname=MongoDB Repository\nbaseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/4.0/x86_64/\ngpgcheck=1\nenabled=1\ngpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc\n

This file will setup the repository that we will be using to bet MongoDB. Once done, you can install the package using yum and get it started like this:

sudo yum install -y mongodb-org\nsudo service mongod start\n

To verify that MongoDB is running, you can enter the MongoDB shell like this:

mongo --host 127.0.0.1:27017\n

You can leave the shell using Ctrl-C. The database and log files will be create at these locations:

/var/log/mongodb\n/var/lib/mongo\n

This is useful to know if you want to make a backup of the database file.

"},{"location":"install/install2/#port-permissions","title":"Port permissions","text":"

On Linux, ports below 1024 are reserved for the root user. This is a security feature. In our case MeshCentral will need to listen to ports 80 and 443. To allow this, we need to allow node to listen to ports below 1024 like this:

whereis node\nnode: /home/ec2-user/.nvm/versions/node/v8.11.3/bin/node\n\nsudo setcap cap_net_bind_service=+ep /home/ec2-user/.nvm/versions/node/v8.11.3/bin/node\n

We first locate the node binary, using whereis node, we then use the setcap command to add permissions to node. Note that we take the path given by whereis and place it in the setcap command. The setcap command will set permissions allowing node to use ports 1024 and below. This permission may be lost when updating the Linux kernel, so this command may need to be applied again in some case.

"},{"location":"install/install2/#installing-meshcentral","title":"Installing MeshCentral","text":"

It\u2019s almost time to install MeshCentral but first, we need to know the public name of our AWS instance, you can run the following command:

curl http://169.254.169.254/latest/meta-data/public-hostname\n

It will return the public name of the AWS instance, for example:

ec2-1-2-3-4.us-west-2.compute.amazonaws.com\n

You can use this name, or if you have another registered DNS name pointing to the server instance, you can also use that now. Note that you must setup any alternative name on your own, MeshCentral will not do this for you. This name must be correct and must resolve to this AWS instance as all mesh agents will use this name to connect back to this server.

Now, we can use the node package manager (NPM) to install MeshCentral.

npm install meshcentral\n

Warning

Do not use sudo in front of npm install meshcentral.

After that, we can run MeshCentral for the first time. We want to run in WAN-only mode since we will not be managing any computers on the same local network at this server. We also want to create a server with a certificate name that is the same at the AWS instance name. So, we will use --wanonly and --cert [name] arguments to get the server started. For example:

node ./node_modules/meshcentral --wanonly --cert ec2-1-2-3-4.us-west-2.compute.amazonaws.com\n
At this point, the server will create its certificates and start running.
MeshCentral HTTP redirection web server running on port 80.\nGenerating certificates, may take a few minutes...\nGenerating root certificate...\nGenerating HTTPS certificate...\nGenerating MeshAgent certificate...\nGenerating Intel AMT MPS certificate...\nGenerating Intel AMT console certificate...\nMeshCentral Intel(R) AMT server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:4433.\nMeshCentral HTTPS web server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:443.\nServer has no users, next new account will be site administrator.\n

You can now open a browser to the name of the server, for example:

https://ec2-1-2-3-4.us-west-2.compute.amazonaws.com\n

You will see the server working as expected. You will get a certificate error since the server is used an untrusted certificate for now. Just ignore the error and see the MeshCentral User\u2019s Guide to fix this.

At this point, the server is usable but, there are two things that may still need to be done. First, if we opted to use MongoDB, we have to configure MeshCentral to use a MongoDB database. By default, NeDB will be used which should only be used for small deployments managing less than 100 computers. We also need to automatically start the server when the AWS instance starts.

To continue, stop the MeshCentral server with CTRL-C.

"},{"location":"install/install2/#configuring-for-mongodb","title":"Configuring for MongoDB","text":"

By default, MeshCentral uses NeDB with a database file located in ~/meshcentral-data/meshcentral.db. This is great for small servers, but if we opted to install MongoDB, let\u2019s make use of it. We need to edit the config.json file located in the meshcentral-data folder.

nano ~/meshcentral-data/config.json\n

Then, make the start of the file look like this:

{\n  \"settings\": {\n    \"MongoDb\": \"mongodb://127.0.0.1:27017/meshcentral\",\n    \"WANonly\": true,\n    \"_Port\": 443,\n    \"_RedirPort\": 80,\n    \"_AllowLoginToken\": true,\n    \"_AllowFraming\": true,\n    \"_WebRTC\": false,\n    \"_ClickOnce\": false,\n    \"_UserAllowedIP\" : \"127.0.0.1,::1,192.168.0.100\"\n  },\n\u2026\n}\n

If you start with the default config.json created by MeshCentral, you will need to remove some _ characters in front of settings, mongodb and wanonly. You can also add a _ to other values.

You can then same the same and run MeshCentral again. This time, you don\u2019t need to specify the certificate name or --wanonly. You just need to run it like this:

node ./node_modules/meshcentral\n

The server should now run correctly and use MongoDB. You can even delete the file ~/meshcentral-data/meshcentral.db as it\u2019s not going to be used anymore. You can check that it runs correctly by browsing to the server\u2019s address again and creating a new account. The first account that is created will be administrator for the server, so don\u2019t delay and create the first account right away.

Once you are done, we can stop the server again using CTRL-C and in the next sections, we will look at starting the server in the background.

"},{"location":"install/install2/#manually-starting-the-server","title":"Manually starting the server","text":"

We can manually start and stop the MeshCentral server in the background in different ways. In this section, we are going to create two commands mcstart and mcstop to take care of this. Type this to create the two commands:

echo \"node ./node_modules/meshcentral > stdout.txt 2> stderr.txt &\" > mcstart\nchmod 755 mcstart\n\necho \"pkill \u2013f node_modules/meshcentral\" > mcstop\nchmod 755 mcstop\n

You can now run the ./mcstart command to launch the server in the background and stop it using the ./mcstop to stop it. This should work pretty well, but if the AWS instance is ever stopped and started again, the server will not automatically launch.

"},{"location":"install/install2/#automatically-starting-the-server","title":"Automatically starting the server","text":"

Since Amazon Linux 2 supports systemd, we are going to use that to auto-start MeshCentral in the background. First, we need to know our own username and group. If we do ls -l in our home folder we get for example:

drwxr-xr-x   2 default default 4096 Jul 20 00:03 Desktop\ndrwxr-xr-x   2 default default 4096 Jul 20 00:03 Documents\ndrwxr-xr-x   2 default default 4096 Jul 20 00:03 Downloads\n\u2026\n

Note the username and group name, in this example it\u2019s default for both. We need this information to create the system service description file. To create this file type:

sudo pico /etc/systemd/system/meshcentral.service\n

Then enter the following lines:

[Unit]\nDescription=MeshCentral Server\n\n[Service]\nType=simple\nLimitNOFILE=1000000\nExecStart=/usr/bin/node /home/default/node_modules/meshcentral\nWorkingDirectory=/home/default\nEnvironment=NODE_ENV=production\nUser=default\nGroup=default\nRestart=always\n# Restart service after 10 seconds if node service crashes\n\nRestartSec=10\n# Set port permissions capability\nAmbientCapabilities=cap_net_bind_service\n\n[Install]\nWantedBy=multi-user.target\n

Note that the user and group values have to be set correctly for your specific situation. Also, the ExecStart and WorkingDirectory lines includes the path to the user\u2019s home folder which includes the username in it. Make sure that is set correctly.

Once this is done, you can now start, enable, stop and disable using the following commands:

sudo systemctl enable meshcentral.service\nsudo systemctl start meshcentral.service\nsudo systemctl stop meshcentral.service\nsudo systemctl disable meshcentral.service\n

Type in the first two commands to start and enable the service. Enabling the service will make it automatically start when the computer restarts.

Once the server is launched, you can access it using a web browser as before. From this point on, refer to the MeshCentral User\u2019s Guide for information on how to configure and use MeshCentral.

"},{"location":"install/install2/#raspberry-pi","title":"Raspberry Pi","text":"

In this section, we will look at installing MeshCentral on the famous Raspberry Pi. This computer\u2019s low price makes it a perfect always-on system for managing computers on a home or small business network. This installation will work on any version of the Raspberry Pi, but version 3 certainly much faster.

For this installation, we are going to use the Raspbian operating system. You can use the NOOBS version to install this operating system on your Raspberry Pi and install Raspbian. For best performance you can use the Raspbian Stretch Lite image which is much smaller and does not have the X desktop interface. To keep things even smaller, we are not going to be installing MongoDB, instead we are just going to be using NeBD as a database that comes by default with MeshCentral.

"},{"location":"install/install2/#installing-nodejs_1","title":"Installing NodeJS","text":"

Start by opening a terminal. For all of the installation, we will assume we are the default pi user and we are in the home (~) folder. Let\u2019s get started by installing NodeJS.

sudo apt-get update\nsudo apt-get dist-upgrade\ncurl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash\nsudo apt-get -y install nodejs\n

We can now check what version of Node was installed by typing:

node -v\n

If all goes well, we can now move on to port permissions and installing MeshCentral itself.

"},{"location":"install/install2/#port-permissions_1","title":"Port permissions","text":"

On Linux, ports below 1024 are reserved for the root user. This is a security feature. In our case MeshCentral will need to listen to ports 80 and 443. To allow this, we need to allow node to listen to ports below 1024 like this:

whereis node\nnode: /usr/bin/node /usr/include/node /usr/share/man/man1/node.1.gz\n\nsudo setcap cap_net_bind_service=+ep /usr/bin/node\n

We first locate the node binary, using whereis node, we then use the setcap command to add permissions to node. Note that we take the path given by whereis and place it in the setcap command. The setcap command will set permissions allowing node to use ports 1024 and below. This permission may be lost when updating the Linux kernel, so this command may need to be applied again in some case.

"},{"location":"install/install2/#installing-meshcentral_1","title":"Installing MeshCentral","text":"

Now, we can use the Node Package Manager (NPM) to install MeshCentral.

npm install meshcentral\n

Warning

Do not use sudo in front of npm install meshcentral.

After that, we can run MeshCentral for the first time. We want to run in WAN-only mode since we will not be managing any computers on the same local network at this server. We also want to create a server with a certificate name that is the same at the AWS instance name. So, we will use --wanonly and --cert [name] arguments to get the server started. For example:

node node_modules/meshcentral --lanonly --fastcert\n

At this point, the server will create its certificates and start running.

MeshCentral HTTP redirection web server running on port 80.\nGenerating certificates, may take a few minutes...\nGenerating root certificate...\nGenerating HTTPS certificate...\nGenerating MeshAgent certificate...\nGenerating Intel AMT MPS certificate...\nGenerating Intel AMT console certificate...\nServer name not configured, running in LAN-only mode.\nMeshCentral HTTPS web server running on port 443.\nServer has no users, next new account will be site administrator.\n

The next step is to get the IP address of the Raspberry Pi. Use ipconfig:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500\n        inet 192.168.2.162  netmask 255.255.255.0  broadcast 192.168.2.255\n        inet6 fe80::8841:34b7:685:14a7  prefixlen 64  scopeid 0x20<link>\n        ether b8:27:eb:01:13:3f  txqueuelen 1000  (Ethernet)\n        RX packets 58325  bytes 72302196 (68.9 MiB)\n        RX errors 0  dropped 271  overruns 0  frame 0\n        TX packets 28457  bytes 3576126 (3.4 MiB)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0\n

You can now open a browser to the name of the server, for example:

https://192.168.2.162\n

You will see the server working as expected. You will get a certificate error since the server is used an untrusted certificate for now. Just ignore the error and see the MeshCentral User\u2019s Guide to fix this.

"},{"location":"install/install2/#configuring-for-lan-only-mode","title":"Configuring for LAN-only mode","text":"

By default, MeshCentral will assume that you are managing devices both on a local network and on the internet. In the case of this Raspberry Pi installation, we only want to manage device on the local network and so, we can configure MeshCentral to do this. It will adapt the server for this usages. To do this, edit the config.json file:

pico ~/meshcentral-data/config.json\n

Then, make the start of the file look like this:

{\n  \"settings\": {\n    \"LANonly\": true,\n    \"FastCert\": true,\n    \"_Port\": 443,\n    \"_RedirPort\": 80,\n    \"_AllowLoginToken\": true,\n    \"_AllowFraming\": true,\n    \"_WebRTC\": false,\n    \"_ClickOnce\": false,\n    \"_UserAllowedIP\" : \"127.0.0.1,::1,192.168.0.100\"\n  },\n\u2026\n}\n

While we are at it, we can put FastCert to true so that RSA2048 certificates are created instead of RSA3072. This is less secure but runs much faster on small processors like the Raspberry Pi. This is the same as specifying `--fastcert\" in the prior section.

"},{"location":"install/install2/#manually-starting-the-server_1","title":"Manually starting the server","text":"

We can manually start and stop the MeshCentral server in the background in different ways. In this section, we are going to create two commands mcstart and mcstop to take care of this. Type this to create the two commands:

echo \"node ./node_modules/meshcentral > stdout.txt 2> stderr.txt &\" > mcstart\nchmod 755 mcstart\n\necho \"pkill -f node_modules/meshcentral\" > mcstop\nchmod 755 mcstop\n

You can now run the ./mcstart command to launch the server in the background and stop it using the ./mcstop to stop it. This should work pretty well, but if the AWS instance is ever stopped and started again, the server will not automatically launch.

"},{"location":"install/install2/#automatically-starting-the-server_1","title":"Automatically starting the server","text":"

Since Raspbian OS supports systemd, we are going to use that to auto-start MeshCentral in the background. First, we need to know our own username and group. If we do ls -l in our home folder we

drwxr-xr-x   2 pi pi  4096 Jul 19 21:23 Desktop\ndrwxr-xr-x   2 pi pi  4096 Jun 26 18:23 Documents\ndrwxr-xr-x   2 pi pi  4096 Jun 26 18:23 Downloads\n\u2026\n

Note the username and group name, in this example it\u2019s pi for both. We need this information to create the system service description file. To create this file type:

sudo nano /etc/systemd/system/meshcentral.service\n
Then enter the following lines:

[Unit]\nDescription=MeshCentral Server\n\n[Service]\nType=simple\nLimitNOFILE=1000000\nExecStart=/usr/bin/node /home/pi/node_modules/meshcentral\nWorkingDirectory=/home/pi\nEnvironment=NODE_ENV=production\nUser=pi\nGroup=pi\nRestart=always\n# Restart service after 10 seconds if node service crashes\nRestartSec=10\n# Set port permissions capability\nAmbientCapabilities=cap_net_bind_service\n\n[Install]\nWantedBy=multi-user.target\n

Note that the user and group values have to be set correctly for your specific situation. Also, the ExecStart and WorkingDirectory lines includes the path to the user\u2019s home folder which includes the username in it. Make sure that is set correctly.

Once this is done, you can now enable, start, stop and disable using the following commands:

sudo systemctl enable meshcentral.service\nsudo systemctl start meshcentral.service\nsudo systemctl stop meshcentral.service\nsudo systemctl disable meshcentral.service\n

Type in the first two commands to start and enable the service. Enabling the service will make it automatically start when the computer restarts.

Once the server is launched, you can access it using a web browser as before. From this point on, refer to the MeshCentral User\u2019s Guide for information on how to configure and use MeshCentral.

"},{"location":"install/install2/#ubuntu-1804","title":"Ubuntu 18.04","text":"

In this section, we will look at installing MeshCentral on Ubuntu 18.04 LTS. This is a long term support of Ubuntu freely available for download at https://www.ubuntu.com. Both the desktop and server versions of Ubuntu will work. If this is a remote server and the desktop will not be needed, the server version of Ubuntu can be used. This section will describe a way to install MeshCentral in a user\u2019s home folder, however there is a more secure way to do it, see Increased Security Installation at the end of this section.

In all cases, MeshCentral must not be installed as root user. It\u2019s not secure and the instructions below will not work correctly.

"},{"location":"install/install2/#installing-nodejs_2","title":"Installing NodeJS","text":"

The first thing to do is get NodeJS installed on the computer. We first install the node version manager then activate it and install the NodeJS LTS. It\u2019s done with 4 commands:

sudo add-apt-repository universe\nsudo apt update\nsudo apt install nodejs -y\nsudo apt install npm -y\n

We can test what version of Node and NPM are installed using:

node \u2013v\nnpm -v\n
"},{"location":"install/install2/#installing-mongodb_1","title":"Installing MongoDB","text":"

If we are going to run a large instance, it\u2019s best to use MongoDB as the database. If you are using a small instance, you can skip installing MongoDB and MeshCentral will use NeDB instead which is a light weight database that is probably great for managing less than 100 computers.

If you want to use MongoDB, we can install MongoDB Community Edition. More information on how to do this for Ubuntu can be found here.

You can install the package using apt and get it started like this:

sudo apt install mongodb -y\n

Then start the Mongodb service in the background and enable it for auto-restart.

sudo systemctl start mongodb\nsudo systemctl enable mongodb\n

To verify that MongoDB is running, you can enter the MongoDB shell like this:

mongo --host 127.0.0.1:27017\n

You can leave the shell using Ctrl-C. The database and log files will be create at these locations:

/var/log/mongodb\n/var/lib/mongo\n

This is useful to know if you want to make a backup of the database file.

"},{"location":"install/install2/#port-permissions_2","title":"Port permissions","text":"

On Linux, ports below 1024 are reserved for the root user. This is a security feature. In our case MeshCentral will need to listen to ports 80 and 443. To allow this, we need to allow node to listen to ports below 1024 like this:

whereis node\nnode: /usr/bin/node /usr/include/node /usr/share/man/man1/node.1.gz\n\nsudo setcap cap_net_bind_service=+ep /usr/bin/node\n

We first locate the node binary, using whereis node, we then use the setcap command to add permissions to node. Note that we take the path given by whereis and place it in the setcap command. The setcap command will set permissions allowing node to use ports 1024 and below. This permission may be lost when updating the Linux kernel, so this command may need to be applied again in some case.

"},{"location":"install/install2/#installing-meshcentral_2","title":"Installing MeshCentral","text":"

Now, we can use the node package manager (NPM) to install MeshCentral.

npm install meshcentral\n

Warning

Do not use sudo in front of npm install meshcentral.

After that, we can run MeshCentral for the first time. For example:

node ./node_modules/meshcentral\n

If the computer has a well-known DNS name that users and agents will use to connect to this server, run MeshCentral like this:

node ./node_modules/meshcentral --cert example.servername.com\n

At this point, the server will create its certificates and start running.

MeshCentral HTTP redirection web server running on port 80.\nGenerating certificates, may take a few minutes...\nGenerating root certificate...\nGenerating HTTPS certificate...\nGenerating MeshAgent certificate...\nGenerating Intel AMT MPS certificate...\nGenerating Intel AMT console certificate...\nMeshCentral Intel(R) AMT server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:4433.\nMeshCentral HTTPS web server running on ec2-54-245-141-130.us-west-2.compute.amazonaws.com:443.\nServer has no users, next new account will be site administrator.\n

You can now open a browser and try the server. If you can on the same computer, you navigate to this URL:

http://localhost\n

If installing on a server that does not have a desktop GUI, use a different computer and enter http:// followed by the IP address or name of the server you installed.

You should see the server working as expected. You will get a certificate error since the server is used an untrusted certificate for now. Just ignore the error and see the MeshCentral User\u2019s Guide to fix this.

At this point, the server is usable but, there are two things that may still need to be done. First, if we opted to use MongoDB, we have to configure MeshCentral to use a MongoDB database. By default, NeDB will be used which should only be used for small deployments managing less than 100 computers. We also need to automatically start the server when the computer starts.

To continue, stop the MeshCentral server with CTRL-C.

"},{"location":"install/install2/#configuring-for-mongodb_1","title":"Configuring for MongoDB","text":"

By default, MeshCentral uses NeDB with a database file located in ~/meshcentral-data/meshcentral.db. This is great for small servers, but if we opted to install MongoDB, let\u2019s make use of it. We need to edit the config.json file located in the meshcentral-data folder.

pico ~/meshcentral-data/config.json\n

Then, make the start of the file look like this:

{\n  \"settings\": {\n    \"MongoDb\": \"mongodb://127.0.0.1:27017/meshcentral\",\n    \"WANonly\": true,\n    \"_Port\": 443,\n    \"_RedirPort\": 80,\n    \"_AllowLoginToken\": true,\n    \"_AllowFraming\": true,\n    \"_WebRTC\": false,\n    \"_ClickOnce\": false,\n    \"_UserAllowedIP\" : \"127.0.0.1,::1,192.168.0.100\"\n  },\n\u2026\n}\n

If you start with the default config.json created by MeshCentral, you will need to remove some _ characters in front of settings, mongodb and wanonly. You can also add a _ to other values. For details on all of the config.json options, including the WANonly option, refer to the MeshCentral User\u2019s Guide.

You can then save the config.json file and run MeshCentral again. This time, you don\u2019t need to specify the certificate name. You just need to run it like this:

node ./node_modules/meshcentral\n

The server should now run correctly and use MongoDB. You can even delete the file ~/meshcentral-data/meshcentral.db as it\u2019s not going to be used anymore. You can check that it runs correctly by browsing to the server\u2019s address again and creating a new account. The first account that is created will be administrator for the server, so don\u2019t delay and create the first account right away.

Once you are done, we can stop the server again using CTRL-C and in the next sections, we will look at starting the server in the background.

"},{"location":"install/install2/#manually-starting-the-server_2","title":"Manually starting the server","text":"

We can manually start and stop the MeshCentral server in the background in different ways. In this section, we are going to create two commands mcstart and mcstop to take care of this. Type this to create the two commands:

echo \"node ./node_modules/meshcentral > stdout.txt 2> stderr.txt &\" > mcstart\nchmod 755 mcstart\n\necho \"pkill \u2013f node_modules/meshcentral\" > mcstop\nchmod 755 mcstop\n

You can now run the ./mcstart command to launch the server in the background and stop it using the ./mcstop to stop it. This should work pretty well, but if the AWS instance is ever stopped and started again, the server will not automatically launch.

"},{"location":"install/install2/#automatically-starting-the-server_2","title":"Automatically starting the server","text":"

Since Ubuntu 18.04 supports systemd, we are going to use that to auto-start MeshCentral in the background. First, we need to know our own username and group. If we do ls -l in our home folder we get for example:

drwxr-xr-x   2 default default 4096 Jul 20 00:03 Desktop\ndrwxr-xr-x   2 default default 4096 Jul 20 00:03 Documents\ndrwxr-xr-x   2 default default 4096 Jul 20 00:03 Downloads\n\u2026\n

Note the username and group name, in this example it\u2019s default for both. We need this information to create the system service description file. To create this file type:

sudo pico /etc/systemd/system/meshcentral.service\n

Then enter the following lines:

[Unit]\nDescription=MeshCentral Server\n\n[Service]\nType=simple\nLimitNOFILE=1000000\nExecStart=/usr/bin/node /home/default/node_modules/meshcentral\nWorkingDirectory=/home/default\nEnvironment=NODE_ENV=production\nUser=default\nGroup=default\nRestart=always\n# Restart service after 10 seconds if node service crashes\nRestartSec=10\n# Set port permissions capability\nAmbientCapabilities=cap_net_bind_service\n\n[Install]\nWantedBy=multi-user.target\n

Note that the user and group values have to be set correctly for your specific situation. Also, the ExecStart and WorkingDirectory lines includes the path to the user\u2019s home folder which includes the username in it. Make sure that is set correctly. Lastly the path to node may need to be changed. Type whereis node to find the correct path.

Once this is done, you can now start, enable, stop and disable using the following commands:

sudo systemctl enable meshcentral.service\nsudo systemctl start meshcentral.service\nsudo systemctl stop meshcentral.service\nsudo systemctl disable meshcentral.service\n

Type in the first two commands to start and enable the service. Enabling the service will make it automatically start when the computer restarts.

Once the server is launched, you can access it using a web browser as before. From this point on, refer to the MeshCentral User\u2019s Guide for information on how to configure and use MeshCentral.

"},{"location":"install/install2/#increased-security-installation","title":"Increased Security Installation","text":"

On Debian based Linux distributions like Ubuntu, a better and more secure way to install MeshCentral is to have it run within a user account this restricted privileges. When installed like this, the self-update capability of MeshCentral will not work. Instead of installing MeshCentral in the user\u2019s home folder, we install it in /opt/meshcentral and we create a meshcentral user that does not have rights to login or change any of the MeshCentral files. To do this, start by creating a new user called meshcentral

sudo useradd -r -d /opt/meshcentral -s /sbin/nologin meshcentral\n

We can then create the installation folder, install and change permissions of the files so that the meshcentral account gets read-only access to the files.

sudo mkdir /opt/meshcentral\ncd /opt/meshcentral\nsudo npm install meshcentral\nsudo -u meshcentral node ./node_modules/meshcentral\n

The last line will run MeshCentral manually and allow it to install any missing modules and create the MeshCentral data folders. Once it\u2019s running, press CTRL-C and continue. The following two lines will change the ownership of files to the meshcentral user and restrict access to the files.

sudo chown -R meshcentral:meshcentral /opt/meshcentral\nsudo chmod -R 755 /opt/meshcentral/meshcentral-*\n

To make this work, you will need to make MeshCentral work with MongoDB because the /meshcentral-data folder will be read-only. In addition, MeshCentral will not be able to update itself since the account does not have write access to the /node_modules files, so the update will have to be manual. First used systemctl to stop the MeshCentral server process, than use this:

cd /opt/meshcentral\nsudo npm install meshcentral\nsudo -u meshcentral node ./node_modules/meshcentral\nsudo chown -R meshcentral:meshcentral /opt/meshcentral\n

This will perform the update to the latest server on NPM and re-set the permissions so that the meshcentral user account has read-only access again. You can then use systemctl to make the server run again.

MeshCentral allows users to upload and download files stores in the server\u2019s meshcentral-files folder. In an increased security setup, we still want the server to be able to read and write files to this folder and we can allow this with:

sudo chmod -R 755 /opt/meshcentral/meshcentral-files\n

If you plan on using the increased security installation along with MeshCentral built-in Let\u2019s Encrypt support you will need to type the following commands to make the letsencrypt folder in meshcentral-data writable.

sudo mkdir /opt/meshcentral/meshcentral-data\nsudo mkdir /opt/meshcentral/meshcentral-data/letsencrypt\nsudo chmod -R 755 /opt/meshcentral/meshcentral-data/letsencrypt\n

This will allow the server to get and periodically update its Let\u2019s Encrypt certificate. If this is not done, the server will generate an ACCES: permission denied exception.

"},{"location":"install/install2/#restore-backup-in-ubuntu","title":"Restore backup in Ubuntu","text":"
  • Stop Meshcentral service sudo systemctl stop meshcentral.service
  • In your old server, get your backup : meshcentral-data folder, and mongodump-xxxx.archive
  • In the new server, replace the actual meshcentral-data with your backup (it will handle your LestEncrypt cert also)
  • Restore mongodb : mongorestore --archive=mongodump-xxxx.archive
  • Restart meshcentral.service sudo systemctl start meshcentral.service
"},{"location":"install/install2/#microsoft-azure_1","title":"Microsoft Azure","text":"

In this section, we will look installing MeshCentral on Microsoft Azure. Microsoft Azure offers many operating system options and we will be selecting Ubuntu Server as our choice. From the Azure portal, we select Virtual machines on the left and Add.

Once you click on Ubuntu Server, you will see a list of available versions. In this example, we selected Ubuntu 18.04 LTS (Long Term Support). We then have to create an instance name and a way to authenticate to the instance.

Next is the type of instance to launch. Any instance will do including the B1s which is the smallest possible instance. Of course, as you manage more computers, using an instance that is a bit more powerful is a good idea.

After selecting the instance type, you can configure storage. 30 gigabytes is plenty. Then the Network Security Group. This is where it\u2019s important to open at least TCP ports 22, 80 and 443.

Optionally if you wish to use the instance with Intel AMT, open port 4433. In addition port 8080 must be open if you are migrating from MeshCentral1 (not typical).

Lastly we launch the instance, it will take a few minutes to setup.

You can then find the public IP address and use a SSH client like PUTTY on Windows to connect to the instance and start getting MeshCentral setup. From this point on, just use the Ubuntu section above to complete the installation.

"},{"location":"install/install2/#google-cloud","title":"Google Cloud","text":"

In this section, we will look installing MeshCentral on Google Cloud. You can sign up easily at https://cloud.google.com/ and you can run a small instance for less than 5$ a month.

Once you have create an account, you can go to the main console and on the left side, go to Compute Engine and create a new VM instance. For our demonstration, we are going to create the smallest instance possible which is a single shared CPU and only 0.6 gigs of RAM.

We select the proper settings and select Ubuntu 18.04 LTS Minimal as the boot operating system. This is convenient as we already covered how to install MeshCentral on this operating system.

Make sure to allow HTTP and HTTPS traffic. Setup like this, we will not be able to manage Intel AMT unless we also open TCP port 4433. Once done with all these options, we can launch the VM instance.

The new instance will take a few minutes to start up. An interesting feature of Google Cloud is that you can access the VM instance shell directly from the web browser. No need for a separate SSH client. This is exactly what we need and we opt to go ahead and option the web console.

If will log you in automatically, no additional credentials needed. We can then follow the Ubuntu 18.04 LTS section above to complete the installation. If you opt for a very small instance, it\u2019s probably a good idea to skip installing MongoDB. Just to get started quickly, we can use the following commands:

sudo apt update\nsudo apt install nodejs -y\nsudo apt install npm -y\nsudo setcap cap_net_bind_service=+ep /usr/bin/node\nnpm install meshcentral\nnode ./node_modules/meshcentral --fastcert \u2013wanonly --cert 35.227.45.84\n

Warning

Do not use sudo in front of npm install meshcentral.

This will install node and npm. Will allow non-root access to ports 80 and 443 and install and start MeshCentral. Because this example uses a very small server instance, we opted to use the fastcert option to create RSA 2048 certificates (the default is RSA 3072 which is more secure).

We use the wantonly option because MeshCentral will not be managing computers on a local network, and for this demonstration just used the external IP address of the instance as the server name.

If you plan on using an instance without the Intel AMT CIRA port being open (TCP 4433), it\u2019s recommended to add --mpsport 0 so to inform MeshCentral that this port is not open and to not offer Intel AMT CIRA features.

Of course, this set of commands is just to get the server started quickly. Follow the Ubuntu 18.04 instructions to setup the server to automatically start using system.

"},{"location":"install/install2/#ubuntu-1604","title":"Ubuntu 16.04","text":"

In this section, we will look at installing MeshCentral on Ubuntu 16.04 LTS. This is the same installation at Ubuntu 18.04 LTS, however you need to install NodeJS in a special way. If you use apt install node, you will get an older version 4.x of NodeJS that will not work with MeshCentral.

"},{"location":"install/install2/#installing-nodejs_3","title":"Installing NodeJS","text":"

The first thing to do is get NodeJS installed on the computer. We first install the node version manager then activate it and install the NodeJS LTS. It\u2019s done with 3 commands:

cd ~\nwget https://deb.nodesource.com/setup_8.x\nsudo bash setup_8.x\nsudo apt-get \u2013y install nodejs\n

We can test what version of Node and NPM are installed using:

node \u2013v\nnpm -v\n

You should see Node version 8 and NPM version 5. At this point, you can continue installing MeshCentral using the Ubuntu 18.04 installation instructions.

"},{"location":"install/install2/#openbsd-64","title":"OpenBSD 6.4","text":"

In this section, we will look at installing MeshCentral on OpenBSD 6.4. This section was originally written by Daulton and placed here with this permission. The original instructions are located at: https://daulton.ca/meshcentral-server-on-openbsd/. The section will setup MeshCentral on non-standard ports HTTPS/3000 and HTTP/3001. Thank you to Daulton for his contribution.

"},{"location":"install/install2/#installing-mongodb_2","title":"Installing MongoDB","text":"

Install the Mongodb package.

pkg_add mongodb\n

Start and enable Mongodb at boot.

rcctl start mongod\nrcctl enable mongod\n

Temporary remount /usr with wxallowed while we compile the port. For Cloud VPS they usually only have a root partition instead of how OpenBSD splits it up by default, you will need to edit /etc/fstab and add wxallowed to the options for the root partition and then reboot. Assure to remove this from the fstab options after you are done.

mount -r -o wxallowed /usr/\n
"},{"location":"install/install2/#installing-nodejs_4","title":"Installing NodeJS","text":"

Install NodeJS from ports as it is not available by a package.

$ cd /tmp\n$ ftp https://cdn.openbsd.org/pub/OpenBSD/$(uname -r)/{ports.tar.gz,SHA256.sig}\n# cd /usr\n# tar xzf /tmp/ports.tar.gz\n# cd /usr/ports/lang/node\n# make install\n# make clean\n
"},{"location":"install/install2/#installing-meshcentral_3","title":"Installing MeshCentral","text":"

Create the MeshCentral user. The parameters used here are important as we will not let this user login, it has no home directory, and its class is set to daemon. In line with the OpenBSD daemon user naming scheme, we preface the username with an underscore _ to make it easily identifiable as a daemon user.

useradd -s /sbin/nologin -d /nonexistent -L daemon -u 446 _meshcentral\n

Let\u2019s install MeshCentral and adjust the permissions.

mkdir -p /usr/local/meshcentral\ncd /usr/local/meshcentral\nnpm install meshcentral\nchown -R _meshcentral:_meshcentral /usr/local/meshcentral\n

Configuring for MongoDB and adjusting some other settings such as the network port. Open up the following config in an editor then, make the start of the file look like below. If the setting does not exist yet, just add it below one of the ones we are adjusting in the main settings block.

If you start with the default config.json created by MeshCentral, you will need to remove some underscore character in front of settings to enable the setting, such as mongodb and wanonly. You can also add an underscore to other values. For details on all of the config.json options, including the WANonly option, refer to the MeshCentral User\u2019s Guide.

Before you can edit the configuration, start the Meshcentral briefly so it generates the default configurations and certificates. Once you see that it says \"MeshCentral HTTPS server running...\", Ctrl-C to exit then edit the configuration file next.

cd /usr/local/meshcentral/node_modules/meshcentral/ && doas -u _meshcentral /usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral/meshcentral.js --launch\n

Edit the MeshCentral config.json. For example using vi:

vi /usr/local/meshcentral/meshcentral-data/config.json\n

In the settings section, set the following key value pairs:

{\n\"settings\": {\n\"Cert\": \"meshcentral.example.com\",\n\"MongoDb\": \"mongodb://127.0.0.1:27017/meshcentral\",\n\"WANonly\": true,\n\"Port\": 3000,\n\"ExactPorts\": true,\n\"RedirPort\": 3001,\n\"allowLoginToken\": true,\n\"allowFraming\": true,\n\"NewAccounts\": 0,\n},\n\u2026\n}\n

Add the following to the root crontab to start MeshCentral at boot. Edit the root crontab by doing the following command as root: crontab -e

@reboot cd /usr/local/meshcentral/node_modules/meshcentral/ && doas -u _meshcentral /usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral/meshcentral.js --launch\n

As root launch Meshcentral while it installs mongojs, once that finishes and Meshcentral launches close it by doing Ctrl-C. Adjust the permissions again as we ran Meshcentral and it generated new files we need to change the ownership of.

/usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral

chown -R _meshcentral:_meshcentral /usr/local/meshcentral\n

Warning

Do not keep this running or use this command in the future to start the Meshcentral server as it starts the server as root!

This is a reference /etc/pf.conf for you to keep your server secure. Add any locally connected networks which should have access and any public IP address of a network which will have client PCs connect from to target_whitelist table. Add your own home and/or business IP to my_own_IPs table.

ext_if = vio0\nset reassemble yes\nset block-policy return\nset loginterface egress\nset ruleset-optimization basic\nset skip on lo\n\nicmp_types = \"{ 0, 8, 3, 4, 11, 30 }\"\n\ntable <target_whitelist> const { 45.63.15.84, 10.18.5.0/24 }\ntable <my_own_IPs> const { 45.63.15.84 }\ntable <bruteforce>\n\nmatch in all scrub (no-df max-mss 1440)\nmatch out all scrub (no-df max-mss 1440)\n\nblock in quick log from urpf-failed label uRPF\nblock quick log from <fail2ban>\n\nblock in from no-route to any\nblock in from urpf-failed to any\nblock in quick on $ext_if from any to 255.255.255.255\nblock in log quick on $ext_if from { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 255.255.255.255/32 } to any antispoof for $ext_if\nblock log all\n\npass in quick inet proto icmp icmp-type $icmp_types\npass in quick inet6 proto icmp6\n\npass in quick proto tcp from <my_own_IPs> \\\nto (egress) port { 22 } \\\nflags S/SA modulate state \\\n(max-src-conn 5, max-src-conn-rate 5/5, overload <bruteforce> flush global)\n\npass in quick inet proto tcp from <target_whitelist> to port 3000\npass in quick inet6 proto tcp from <target_whitelist> to port 3000\n\nblock in quick log on egress all\n\npass out quick on egress proto tcp from any to any modulate state\npass out quick on egress proto udp from any to any keep state\npass out quick on egress proto icmp from any to any keep state\npass out quick on egress proto icmp6 from any to any keep state\n

After saving the configuration in /etc/pf.conf, reload the pf rules with:

pfctl -f /etc/pf.conf\n

To save rebooting and have MeshCentral launch then, launch it so you can begin using it. This time it is running as _meshcentral, now it is safe to keep running and you can use this command in the future.

cd /usr/local/meshcentral/node_modules/meshcentral/ && doas -u _meshcentral /usr/local/bin/node /usr/local/meshcentral/node_modules/meshcentral/meshcentral.js --launch\n

You can now access MeshCentral at https://youraddress:3000 or https://meshcentral.example.com:3000 if you named the machine meshcentral or create an A record named meshcentral. The first user you create will be the Administrator, there is no default user.

"},{"location":"intelamt/","title":"Intel AMT","text":"

Intel AMT Guide as .odt

"},{"location":"intelamt/#video-walkthru","title":"Video Walkthru","text":""},{"location":"intelamt/#abstract","title":"Abstract","text":"

This user guide contains all essential information for activating and using Intel\u00ae Active Management Technology (Intel\u00ae AMT) with MeshCentral. We will review how to activate, connect to and use Intel AMT features and how this benefit administrators that want to manage computers remotely. This document expect the reader to already be familiar with how to install and operate MeshCentral and have a basic understanding of how Intel\u00ae AMT works.

"},{"location":"intelamt/#history-of-amt","title":"History of AMT","text":""},{"location":"intelamt/#introduction","title":"Introduction","text":"

MeshCentral is a free open source web-based remote computer management software and it fully supports Intel\u00ae Active Management Technology (Intel\u00ae AMT). MeshCentral does not require that computers it manages support Intel AMT, but if a remote computer has this capability, MeshCentral will make use of it.

Intel AMT can be seen as a hardware based management agent that is built into some Intel PC\u2019s. Once setup, Intel AMT can be used to remotely manage a computer regardless of the operating system health. It can be used to power on a computer when it\u2019s in soft-off state or to provide enhanced monitoring and security to remote systems.

Once setup, a computer can have up to management connections to MeshCentral. One of them by the Mesh Agent that lives in the operating system and another connection from Intel AMT. When remote management is made using an operating system agent, we call this \u201cin-band management\u201d and when management is done using a hardware based agent like Intel AMT, we call this \u201cout-of-band management\u201d

MeshCentral can support computers that have either or both agents. So, you can setup a computer with just the Mesh Agent, just Intel AMT or both. In this document we will show how to install computers with both agent connections or with just Intel AMT. When Intel AMT is used alone, we call this \u201cagent-less\u201d as there will be no operating system software required to remotely manage the computer.

The Mesh Agent and Intel\u00ae AMT have very different and complementary capabilities and so, it\u2019s often beneficial to use both and one will offer features the other can\u2019t provide. Here are some of the benefits each has to offer:

Mesh Agent

  • Fast remote desktop / clipboard access.
  • Remote access to operating system files.
  • Remote chat and other OS features.

Intel\u00ae AMT

  • Remote desktop even when the agent or operating system is not functional.
  • Remote access to BIOS.
  • Connectivity when soft-off / sleeping.
  • Remote power actions.

If you are looking into managing remote computers that would be difficult to physically get access to for remote support or maintenance, one should probably look at getting a PC with Intel AMT.

"},{"location":"intelamt/#bare-metal-activation-server","title":"Bare-Metal Activation Server","text":"

The AmtProvisioningServer section in the settings section of the config.json will enable this feature. MeshCentral will then listen for activation requests, match against your ACM activation certificates and if everything goes well, will activate and add the device to a Intel AMT only device group. No agent or MeshCMD is involved.

This bare-metal activation server is not enabled by default and only makes sense when activating devices on the local network.

Once enabled, Intel AMT can send \u201chello\u201d data to the MeshCentral provisioning server on port 9971 and MeshCentral will respond by connecting back, authenticating, and activating Intel AMT. MeshCentral will then log the event, add the device to a pre-defined agent-less device group and complete any remaining configuration. A trusted CA certificate is required to perform this operation fully automatically.

"},{"location":"intelamt/#meshcentral-group-types","title":"MeshCentral Group Types","text":"

Once MeshCentral is installed, a user will typically create a new device group. Here is the first hint that MeshCentral supports Intel AMT. Device groups come in two types. You can manage using a software agent, or using Intel AMT only.

Note that if you use the OS agent to manage computers, you can also set and use Intel AMT. However, if you opt to create an Intel AMT only group, then Mesh Agents are not supported. One can create groups of both types in order to manage devices that have and don\u2019t have the Mesh Agent installed.

The main benefit of \u201cIntel AMT only\u201d group is if someone does not want to install a background agent on remote systems or already have a remote management solution and intends to only use MeshCentral to supplement the existing solution with Intel AMT features.

Once a group is created, the links MeshCentral provides to on-board devices will change depending on the group type and how the server is setup. The device on-boarding links are located in the \u201cMy Devices\u201d page, next to the group name.

If the MeshCentral server is setup in \u201cLAN mode\u201d or \u201cHybrid mode\u201d, options will be available to add computers on the local network. If you have an Intel AMT computer that is already activated, you can select the \u201cAdd Local\u201d or \u201cScan Network\u201d options in the \u201cIntel AMT only\u201d group type and start adding local network computers this way. If MeshCentral is in \u201cWAN mode\u201d, you will need to setup Intel AMT to connect back to MeshCentral using a feature called \u201cClient Initiated Remote Access\u201d or CIRA for short. We will cover that in a later section.

"},{"location":"intelamt/#client-initiated-remote-access-mps-server","title":"Client Initiated Remote Access & MPS server","text":"

Client Initiated Remote Access (CIRA) is a feature of Intel AMT that, then configured, makes Intel AMT connect back to the server using a TLS tunneling connection similar with a SSH tunnel. Once this tunnel connection is established, the server can perform remote management operations on Intel AMT.

CIRA is great when remotely managing Intel AMT devices over the Internet thru network address translator (NAT) routers where the server would not be able to connect to Intel AMT. This is similar to the Mesh Agent that initiated and keeps an idle connection to the server.

By default, MeshCentral will be configured to receive Mesh Agent connections on TCP port 443 and Intel AMT connections on TCP port 4433. These port values can be configured in the config.json file of MeshCentral.

Once connected to port 443, the Mesh agent will using secure HTTPS WebSocket to securely communicate with the server. Intel AMT will use TLS to connect to port 4433 and use a binary tunneling protocol called the Intel AMT Port Forwarding Protocol (APF). You can find documentation on this protocol at the following URL:

https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/MPSDocuments/Intel%20AMT%20Port%20Forwarding%20Protocol%20Reference%20Manual.pdf

It\u2019s not necessary to know or understand the details of this protocol, MeshCentral will take care of handling this. In Intel AMT nomenclature, the server that receives a CIRA connection is called a \u201cManagement Presence Server\u201d or MPS for short. In other words, MeshCentral has a MPS server on port 4433 ready to receive Intel AMT CIRA connections.

When MeshCentral is first setup, a self-signed root certificate is created along with a MPS certificate that will be presented when a device connects on port 4433. There is typically no need to use a CA signed & trusted certificate on port 4433 was we only expect Intel AMT computers to connect to this port and we will be loading our self-signed root in Intel AMT for authentication purposes.

One way to check that the MeshCentral MPS server is running correctly is to use a browser and access port 4433 using HTTPS. The browser will display a warning because the port 4433 certificate is not trusted, but this is expected.

The CIRA protocol is binary, but MeshCentral will detect that the request is made from a browser and return a short message:

MeshCentral2 MPS server.\nIntel\u00ae AMT computers should connect here.\n

This is practical to make sure connectivity with the MeshCentral MPS server is working. Now that we know the basics of Intel AMT CIRA and the MPS server, we can configure Intel AMT to connect.

"},{"location":"intelamt/#activation-certificate-setup","title":"Activation Certificate Setup","text":"

If you have an Intel AMT activation certificate, you should configure MeshCentral to take advantage of it. Your activation certificate must have been issued by one of the certificate authorities (CA\u2019s) that is trusted by Intel AMT and MeshCentral will need the entire certificate chain to be provided since the entire chain is needed to perform Intel AMT ACM activation.

The leaf certificate will have the Intel AMT activation option and a specific domain name while the hash of the trusted CA certificate must be trusted by Intel AMT. The certificate chain will have to be setup in the domain section of the MeshCentral config.json file.

If you have a certificate chain in a .pfx or .p12 format, place that file in the \u201cmeshcentral-data\u201d folder and add the \u201cAmtAcmActivation\u201d section in the domain section like so:

{\n  \"settings\": {\n    \"Cert\": \"devbox.mesh.meshcentral.com\",\n  },\n  \"domains\": {\n    \"\": {\n      \"title\": \"My Server\",\n      \"AmtAcmActivation\": {\n        \"log\": \"amtactivation.log\",\n        \"certs\": {\n          \"myamtcert\": {\n            \"certpfx\": \"amtcert.pfx\",\n            \"certpfxpass\": \"pfxpassword\"\n          }\n        }\n      }\n    }\n}\n

If you have the certificate chain in PEM format as a set of .crt files and a .key file, start by placing all of the certificate files in the \u201cmeshcentral-data\u201d folder and setup the certificate chain like this:

{\n  \"settings\": {\n    \"Cert\": \"devbox.mesh.meshcentral.com\",\n  },\n  \"domains\": {\n    \"\": {\n      \"title\": \"My Server\",\n      \"AmtAcmActivation\": {\n        \"log\": \"amtactivation.log\",\n        \"certs\": {\n          \"myvprocert\": {\n            \"certfiles\": [ \"amtacm-vprodemo.crt\",\n   \"amtacm-intermediate1.crt\",\n   \"amtacm-intermediate2.crt\",\n   \"amtacm-root.crt\" ],\n            \"keyfile\": \"amtacm-vprodemo.key\"\n          }\n        }\n      }\n    }\n}\n

It\u2019s important that the leaf certificate file be the first file in the \u201ccertfiles\u201d array. The order of the other certificates is not important as MeshCentral will figure out and re-order them correctly.

Within the new \u201cAmtAcmActivation\u201d section, there is a \u201clog\u201d entry. This is a log file that will contain every activation attempt including the details of the computer being activation and what Intel AMT administrator password was used for activation. This log file should be kept securely as it will contain Intel AMT credentials. It\u2019s also important to have this file as a backup so that Intel AMT credentials are not lost after activation. If MeshCentral can\u2019t write to this log, the activation will not go forward and will fail.

Once the config.json was modified, restart the server. There will be two indications that the server has the new certificate correctly configured. For \u201cIntel AMT only\u201d groups, a new \u201cActivation\u201d link will show up. Clicking this link will show a command that can be run to perform ACM activation.

For device groups that operate with a Mesh Agent, you can edit the group and select the \u201cSimple Admin Control Mode\u201d Intel AMT activation policy. This policy is not available unless a correct Intel AMT ACM activation certificate is configured.

Once setup, Intel AMT will not automatically activate to Intel AMT unless the right situation is met. The Intel AMT activation certificate is for a specific domain name suffix, for example \u201cmeshcentral.com\u201d. Intel AMT must be in a situation where this domain can be accepted. One of the following must be true:

  • Intel AMT must have a wired Ethernet interface that is connected to a local network with a DHCP server responding with option 15 set to \u201cxxx.meshcentral.com\u201d.
  • The name \u201cmeshcentral.com\u201d by have been set as \u201cTrusted FQDN\u201d in MEBx.
  • The name \u201cmeshcentral.com\u201d must have been set using a USB key with a setup.bin file.

Once Intel AMT is in a situation where ACM activation can occur, the activation command line can be run or the Mesh Agent will detect this situation and ask the server to perform activation.

The best way to test this feature is to create an \u201cIntel AMT only\u201d device group and run the MeshCMD command on the remote system to perform activation. If there is a problem, this process should clearly display why ACM activation fails.

Note

Activation over wifi has some additional issues. First you need to add your WiFi access point to that wifi configuration to allow CSME to take over WiFi when OS is not functioning. Then it should work. Please also make sure you install Intel WiFi driver and Intel LMS package. It should work. You can detach the ethernet and then try connecting to that device using the IP address acquired by WiFi interface. See Open AMT Cloud Toolkit project - a close relative to this project. It has an AMT activation component and newer remote provisioning client can activate locally and also can manage Wi-Fi profile.

"},{"location":"intelamt/#intel-amt-mei-and-lms","title":"Intel AMT MEI and LMS","text":"

Intel Active Management Technology (Intel AMT) can communicate to the local platform using the Management Engine Interface (MEI). We show how your can use that to get Intel AMT information. For more advanced usages, you need to connect using TCP and TLS which requires Intel Local Manageability Service (LMS). We show how MeshCentral's Mesh Agent and MeshCMD have a small version of LMS built-in and how it works

"},{"location":"intelamt/#intel-amt-system-defense","title":"Intel AMT System Defense","text":"

As part of Intel AMT there are hardware filters in the network interface you can setup to match and perform actions on packets. This happens at Ethernet speeds with no slow down and independent of the OS.

"},{"location":"meshcentral/","title":"Meshcentral2 Guide","text":"

MeshCentral2 Guide

MeshCmd Guide as .pdf as .odt

"},{"location":"meshcentral/#video-walkthru","title":"Video Walkthru","text":""},{"location":"meshcentral/#abstract","title":"Abstract","text":"

This user guide contains all essential information for the user to make full use of MeshCentral, a free open source web-based remote computer management software. The guide provides quick steps to setup administrative groups to remote control and manage computers in local network environments or via the Internet. Latter parts of the document will cover some advanced topics. The reader is expected to already have some of the basic understanding on computer networking, operating system and network security.

"},{"location":"meshcentral/#introduction","title":"Introduction","text":"

MeshCentral is a free open source web-based remote computer management software. You could setup your own management server on a local network or on the internet and remote control and manage computers that runs either Windows* or Linux* OS.

To begin, a base or management server will be required. A management server could be any computing device (PC or VM) that has sufficient compute, storage and reliable network components to host an environment for MeshCentral and deliver good performance during remote management exercise. Whilst there are many configurations available for advanced users, typical server setup would only take just a few minutes to complete.

At a high level, there are only four (4) main steps: Setup, Install, Connect and Control.

  1. Setup the MeshCentral server on VM or PC
  2. Log on to MeshCentral portal with a valid account, creates an administrative mesh to collect all end-points (systems to be managed)
  3. Generates an agent and installs it on a target or each end-point that immediately attempts a connection back to MeshCentral server.
  4. Controls/manages assets or end-points that are available in respective administrative mesh
"},{"location":"meshcentral/#server-installation","title":"Server Installation","text":"

Because the MeshCentral server is written in NodeJS it can be installed on many operating systems including Windows, Linux. Please refer to the MeshCentral Installer\u2019s Guide available at https://www.meshcommander.com/meshcentral2 for information on how to install the server.

The server can be installed both on a local area network for local computer management and in the cloud for management of computers over the Internet. You can also install it on small IoT devices like a Raspberry Pi all the way to big servers. It\u2019s recommended to get started with a test setup to get a feel for this server. Once installed, come back to this document for configuring and using your new server.

"},{"location":"meshcentral/#basic-usage","title":"Basic Usage","text":"

In this section we will cover the basics of MeshCentral in your newly setup server.

"},{"location":"meshcentral/#launch","title":"Launch","text":"

Start your web browser and access MeshCentral via IP address/URL, http://serverFQDN/. If MeshCentral is running locally, enter http://127.0.0.1/. MeshCentral will redirect the browser to HTTPS if the server was accessed with HTTP. Once on HTTPS you will likely see this message:

This is because by default MeshCentral is using a self-signed certificate that is not known to the browser as a \u201ctrusted\u201d or \u201ctrustworthy\u201d certificate. To prevent this warning from recurring, the following chapter will provide useful steps that can be considered.

To proceed on Firefox browser,

  • Click on \u201cAdvanced\u201d, \u201cAdd Exception\u201d and \u201cConfirm Security Exception\u201d

To proceed on Chrome Browser,

  • Click on \u201cAdvanced\u201d, \u201cProceed to http://serverIP (unsafe)\u201d

Note: You can also get to a device by specifying the device name in the URL by adding ?viewmode=10&gotodevicename=MyComputer to the URL of the MeshCentral web page. The new gotodevicename will find a device with the specified name and navigate to that device\u2019s page. This is perfect for integrating MeshCentral with other solutions but be aware that a computer name is not a unique identifier and so, &gotonode= is always the preferred way to access a device. This feature also works on the mobile web site.

"},{"location":"meshcentral/#create-account","title":"Create Account","text":"

Create an account by clicking \u201cCreate One\u201d and click \u201cCreate Account\u201d once the text fields had been populated correctly.

"},{"location":"meshcentral/#new-device-group","title":"New device group","text":"

Once logged in, create a new device group. This is a group of computers that we want to manage. To proceed,

  1. Click on \u201cClick here to create a new group of devices\u201d,
  2. Key in a suitable \u201cName\u201d, .e.g. \u201cSampleGroup\u201d
  3. Leave \u201cType\u201d to default \u201cManage using a software agent\u201d and click \u2018OK\u201d.

Note

There are two types of groups: Software Agent Group: Commonly used to manage computers. Administrator must install a \u201cremote management agent\u201d on the remote computers. Intel\u00ae AMT Agent-less Group: Exclusive for remote computers that has Intel\u00ae AMT activated and needs to be managed independent of a \u201cremote management agent\u201d.

"},{"location":"meshcentral/#add-device","title":"Add device","text":"

To add devices into new mesh

  1. Click \u201cAdd Agent\u201d,
  2. Select the right Operating Systems (Windows* OS) and download the Mesh Agent executable.
  3. Copy the Mesh Agent file into remote computers with Windows* OS
  4. Run Mesh Agent and Click \u201cinstall\u201d

Note

Mesh Agent is available for Windows* and Linux*. For Windows*, the mesh agent doesn\u2019t contain any sensitive data and can copied and reused on many Windows* computers. For Linux*, instead of an executable, an installation script is provided to add remote computers. The script checks the type of computer and installs the proper agent automatically.

"},{"location":"meshcentral/#after-agent-install","title":"After agent install","text":"

Once the agents are installed, it will take up to a minute before the computer shows up on the user\u2019s account automatically. Click on each computer to access it and user can rename the each computer with a unique name and icons.

"},{"location":"meshcentral/#manage-computer","title":"Manage Computer","text":"

Click on any computer and go into the \u201cDesktop\u201d and \u201cFiles\u201d tabs to remotely manage the computer or perform file transfer.

For advance users with console/command line interface experience, go into \u201cTerminal\u201d to perform scripting or quick tasks with CLI tools.

"},{"location":"meshcentral/#desktop-control","title":"Desktop Control","text":"

Depending on how the agent is connected to the server, there are multiple methods to remote control. Mesh Agent, RDP, and AMT

For RDP connections, if you have previously saved the credentials that is usable by all users on the system. If you want to remove those saved credentials that's under the General Tab > Credentials. Click pen to clear them.

"},{"location":"meshcentral/#server-certificate","title":"Server Certificate","text":"

As seen in the previous chapter, MeshCentral is setup with a self-signed certificate by default and the web browser will issue a warning concerning the validity of the certificate.

Users have few ways to handle this certificate warning:

  • Ignore the warning and proceed with an exception in a recurring fashion. However, traffic from the server to the web browser remains encrypted. User must check the validity of the certificate presented by the website and compare with \u201cwebserver-cert-public.crt\u201d file in the \u201cmeshcentral-data\u201d folder of the server.
  • Add webserver\u2019s root certificate into web browser\u2019s trust list. Click on \u201cRoot Certificate\u201d link at the bottom right of login page to download the root certificate of the web server and then add/import this as a trusted certificate into web browser. Some web browser may require a restart before the certificate installation takes effect.
  • If you own a domain name that points to your MeshCentral server, you can get a free trusted certificate using Let\u2019s Encrypt (https://letsencrypt.org/). See the section on Let\u2019s Encrypt in this document for more information on this option. MeshCentral has built-in support for Let\u2019 Encrypt.

Important

Before adding/importing the certificate, user must check the validity of the certificate presented by the website and compare with \u201croot-cert-public.crt\u201d file in the \u201cmeshcentral-data\u201d folder of the server.

For large scale deployments or setup, a legitimate trusted certificate is highly recommended for your web server. This way, any web browser that navigates to this web server will be able to readily verify its authenticity.

  • If a legitimate trusted certificate is available, replace \u201cwebserver-cert-public.crt\u201d and \u201cwebserver-cert-private.key\u201d with your certificate. These files are located in \u201cmeshcentral-data\u201d folder of the server.
  • If intermediate certificates are needed, add the files \u201cwebserver-cert-chain1.crt\u201d, \u201cwebserver-cert-chain2.crt\u201d, \u201cwebserver-cert-chain3.crt\u201d respectively with the intermediate certificates.

Note: If you are using TLS offloading, see the section on \u201cTLS Offloading\u201d cover in the latter parts of this document.

"},{"location":"meshcentral/#files-and-folder-structure","title":"Files and Folder Structure","text":"

It\u2019s important to know the basic file and folder structure from which MeshCentral was installed as shown below

Right after running the \u201cnpm install meshcentral\u201d command, the node_module folder will be created which contains meshcentral and all of its dependent modules. When the server executes for the first time, both meshcentral-data and meshcentral-files folders will be created.

Important

User must periodically backup both meshcentral-data and meshcentral-files which contains all of server\u2019s data.

The \u201cmeshcentral-data\u201d folder will contain:

meshcentral.db file: The server\u2019s database file which contains all of the user and computer information. This includes account information and other sensitive information.

Five .key and .crt files: These are the server\u2019s certificates and private keys. They are used to securely identify the server. The .key files must not be obtained by anyone else since they could be used to impersonate the server.

config.json file: This is the server\u2019s configuration file. It first starts with a sample configuration that you can change. In a following section, we will discuss how to edit this file to customize the server.

The \u201cmeshcentral-files\u201d folder contains user files that have been uploaded to the server. This folder can be quite large, especially if no user space quota is set in the config.json file. Users can upload a significant amount of files on the server.

Important

Back-up the \u201cmeshcentral-data\u201d folder since this is the folder needed to reconstruct the server if something goes wrong. Without it, user will to start over. Recommended to apply suitable encryption on both folders given that they contain sensitive data.

"},{"location":"meshcentral/#server-configuration-file","title":"Server Configuration File","text":"

In the \u201cmeshcentral-data\u201d folder, there is a file called config.json that contains the main configuration of the server. A sample configuration file could look like this:

    {\n        \"settings\": {\n            \"cert\": \"mesh.myserver.com\",\n            \"port\": 8080,\n            \"redirport\": 81\n        },\n        \"domains\": {\n            \"\": {\n                \"title\": \"MyServer\",\n                \"title2\": \"Servername\",\n                \"userQuota\": 1048576,\n                \"meshQuota\": 248576,\n                \"newAccounts\" : 1\n            },\n            \"Customer1\": {\n                \"title\": \"Customer1\",\n                \"title2\": \"Extra String\",\n                \"newAccounts\" : 0\n            }\n        },\n        \"peers\": {\n            \"serverId\" : \"Server1\",\n            \"servers\": {\n                \"Server1\": { \"url\": \"wss://192.168.1.100:443/\" },\n                \"Server2\": { \"url\": \"wss://192.168.1.101:443/\" }\n            }\n        }\n    }\n

First, we will look at each of the top levels of the configuration file. The tops levels are \u201csettings\u201d, \u201cdomains\u201d, \u201cpeers\u201d, and \u201csmtp\u201d as shown in the table below.

"},{"location":"meshcentral/#settings","title":"Settings","text":"

As indicated before, the settings section of the config.json is equivalent to passing arguments to the server at runtime. Below is a list of settings that are available for the user.

Settings Option Description Cert Sets the DNS name of the server. If this name is not set, the server will run in \"LAN mode\". When set, the server\"s web certificate will use this name and the server will instruct agents and browsers to connect to that DNS name. You must set a server DNS name to run in \"WAN mode\". MeshCentral will not configure your DNS server. The DNS name must be configured separately. Port This sets the main web port used by the MeshCentral server and it\"s the same port that users and mesh agents will connect to. The default port is 443, but if the port is busy, the next available higher port is used (.e.g. 444) AliasPort Sets the main port that will be used by the server externally. By default is the same as \"Port\" above, but can be set to be different when next. See \"Server port aliasing\" section for more details. RedirPort This is the port for redirecting traffic in the web server. When the server is configured with HTTPS, users that uses HTTP will be redirected to HTTPS. Port 80 is the default port. So, redirection will happen from port 80 to port 443. MpsPort Port for Intel\" AMT Management Presence Server to receive Intel\" AMT CIRA (Client Initiated Remote Access) connections. The default is port 4433. This port is disabled in LAN mode. If user don\"t plan on using Intel\" AMT for management, this port can be left as-is. TLSOffload By default this option is set to \"false\". If set to \"true\", server will run both web port and the Intel AMT MPS port without TLS with the assumption that a TLS offloading is taking care of this task. For further details, see the \"TLS Offloading\" section. This option can also be set to the IP address of the reverse-proxy in order to indicate to MeshCental to only trust HTTP X-Forwarded headers coming from this IP address. See the \"Reverse-Proxy Setup\" section for an example. SelfUpdate When set to \"true\" the server will check for a new version and attempt to self-update automatically a bit after midnight local time every day. If set to a specific version such as \"1.1.21\" the server will immediately update to the specified version on startup if it's not already at this version. SessionKey This is the encryption key used to secure the user\"s login session. It will encrypt the browser cookie. By default, this value is randomly generated each time the server starts. If many servers are used with a load balancer, all servers should use the same session key. In addition, one can set this key so that when the server restarts, users do not need to re-login to the server. Minify Default value is 0, when set to 1 the server will serve \"minified\" web pages, that is, web pages that have all comments, white spaces and other unused characters removed. This reduces the data size of the web pages by about half and reduced the number requests made by the browser. The source code of the web page will not be easily readable, adding \"&nominify=1\" at the end of the URL will override this option. User Specify a username that browsers will be automatically logged in as. Useful to skip the login page and password prompts. Used heavily during development of MeshCentral. NoUsers By default this option is \"false\" and if set to \"true\", server will only accept users from localhost (127.0.0.1) and will not have a login page. Instead, a single user is always logged in. This mode is useful if user opts to setup MeshCentral as a local tool instead of as a multi-user server MpsCert Specifies the official name of the Intel AMT MPS server. If not specified, this is the same as the official server name specified by \"cert\". This option is generally used with MPS aliasing, see the \"Server port aliasing\" section for more information. MpsAliasPort Specify an alias port for the MPS server. See the section on \"Server port aliasing\" for use of this option. ExactPorts If this option is set to \"true\", only the exact port will be used. By default, if a port is in use, the server will try to bind the next available higher port. This is true for the \"port\", \"redirport\" and \"mpsport\" settings. Lanonly Server\"s default mode if not set with \"--cert\" option. If this option is set to \"true\", Intel\" AMT MPS will be disabled, server name and fixed IP option will be hidden. Mesh agents will search for the server using multicast on the network. Wanonly A recommended option when running MeshCentral in the cloud. If set to \"true\", server will run as a cloud service and assumes LAN features are disabled. For this option to work, the server must have a fixed IP or DNS record using the \"--cert\"\" option. In this mode, LAN discovery features are disabled. AllowFraming By default is set to \"false\". If set to \"true\", web pages will be served in a way that allows them to be placed within an iframe of another web page. This is useful when you wish to add MeshCentral features into another website. AllowLoginToken By default is set to \"false\". If set to \"true\", the server allows login tokens to be used in the URL as a replacement for user login. This is useful along with \"allowFraming\" option to embed MeshCentral features into another website. MongoDB Used to specify the MongoDB connection string. If not specified, MeshCentral will use the NeDB database with the file meshcentral.db in the meshcentral-data folder. To setup MongoDB, please refer to the Database section of this document. MongoDBCol Used to specify the MongoDB collection name in the database. By default this value is \"meshcentral\". See Database section for more details on MongoDB setup. DbEncryptKey Specifies a password used to encrypt the database when NeDB is in use. If wanting to encrypt an existing database, use the \"dbexport\" and \"dbimport\" to save and reload the database with the encryption password set. WebRTC Set to \"true\" or \"false\" depending if you want to allow the server to setup WebRTC communication. If WebRTC is setup, management traffic will flow directly between the browser and mesh agent, bypassing the server completely. The default is false now, but will be switched to true when WebRTC is ready for production. ClickOnce Set to \"true\" or \"false\" to allow or disallow browser ClickOnce features. When enabled, browsers running on Windows will be shown extra options to allow RDP and other sessions thru the MeshCentral server. This requires ClickOnce browser support that is built-in to IE and available as add-in to Chrome and Firefox. Default is true.

Important

Changes in config.json will NOT take effect until server is restarted.

Note: We recommend the user to use a non-production server to experiment the setting options above.

"},{"location":"meshcentral/#domains","title":"Domains","text":"

In the domains section, you can set options for the default domain (\"\") in addition to creating new domains to establish a multi-tenancy server. For standard configuration, the root domain and other domains will be accessible like this:

https://servername:8080/ <- default domain

https://servername:8080/customer1 <- customer1 domain

https://servername:8080/customer2 <- customer2 domain

When a user setup many domains, the server considers each domain separately and each domain has separate user accounts, administrators, etc. If a domain has no users, the first created account will be administrator for that domain. Each domain has sub-settings as follows:

Sub Settings Description Title & Title2 This are the strings that will be displayed at the banner of the website. By default title is set to \u201cMeshCentral\u201d and title2 is set to a version number UserQuota This is the maximum amount of data in kilobytes that can be placed in the \u201cMy Files\u201d tab for a user account. MeshQuota This is the maximum amount of data in kilobytes that can be placed in the \u201cMy Files\u201d tab for a given mesh NewAccounts If set to zero (0) UserAllowedIP Allows user to set a list of allowed IP addresses. See section on server IP filtering. Auth This mode is often used in corporate environments. When server is running on Windows and this value is set to \u201csspi\u201d, domain control authentication to the website is performed. In this mode, no login screen is displayed and browser will authenticate using the user\u2019s domain credentials. Dns The DNS record for this domain. If specified, the domain is accessed using a DNS record like \u201ccustomer1.servername.com\u201d instead of \u201cservername/customer1\u201d. This feature requires the DNS server to be configured to point this server with a valid DNS record. CertUrl Load the TLS certificate for this domain from this https url. For example \u201chttps://127.0.0.1:123\u201d. This option is useful when used along with the \u201cTlsOffload\u201d option. When MeshCentral is not doing any TLS but has a reverse-proxy or TLS offload device doing this work in front of the server, you can use this to have MeshCentral load the certificate from the server in front of MeshCentral.This is needed because when agents connect, they need to be told that the certificate they saw upon connecting is the correct one. Using this, MeshCentral will know what certificate the agents are expected to see. PasswordRequirements Used to specify the minimum password requirements for user authentication to this domain. By default, no password requirements are enforced but the user will see a password strength indicator that is not backed by any verifiable data.The value must be set to an object, for example:{ \"\"min\"\": 8, \"\"max\"\": 128, \"\"upper\"\": 1, \"\"lower\"\": 1, \"\"numeric\"\": 1, \"\"nonalpha\"\": 1 }This indicated that passwords must be at least 8 characters long and have at least one upper case, one lower case, one numeric and one non-alphanumeric character. You can also set the maximum length of the password, however MeshCentral has already a limit of 256 characters. Specifying anything above this will have no effect.Note that password requirements for Intel\u00ae AMT are defined by Intel and so, Intel\u00ae AMT passwords will always be verified using a separate set of requirements.

Note: When the DNS value is set for a domain, user can\u2019t access the domain using \u201cservername/customer1\u201d instead it must be accessed with the valid DNS record and the DNS server should be setup to have two or more DNS records pointing to the same IP address.

In this mode, the server will serve a different TLS certificate depending on what DNS record is used to access the server.

As shown in the example above, we have two names that point to the same IP address. Since the configuration specifies the \u201cdns\u201d value, the second domain is only shown when the right name is used. We use \u201cmeshcentral\u201d and \u201cdevbox\u201d for DNS names, but in practice the user will use fully qualified domain names (FQDN) like \u201cmeshcentral.com\u201d or \u201cdevbox.meshcentral.com\u201d.

"},{"location":"meshcentral/#server-peering","title":"Server Peering","text":"

MeshCentral supports server peering. User could setup up many servers to share the task of handling incoming connections from managed clients and consoles. For server peering to function, all servers must have access to the same database, use the same certificates, the same configuration (with the exception of the server name) and servers must be able to communicate with each other behind a load balancer.

Hence, the user is expected to have good understanding on networking, server administration and\u00a0applications to accomplish this setup. This document will not get into the details of setting up a load-balancer.

Recommended

Before setting up MeshCentral peering, database migration from NeDB database to MongoDB with replication/sharding option enabled is highly recommend. See: Setting up MeshCentral with MongoDB (section 8.4)

The setup flow above guides the user to pull together server peering setup with Meshcentral. (2) Shared storage is compulsory to host user files and it must be accessible from all of the servers. If the server is expected for critical work, replicated shared storage should be considered.

When Meshcentral is ready for peering setup (5), replicate the \u201cmeshcentral-data\u201d directory on each server and configure the \u201cpeers\u201d section of the config.json file as shown below.

    {\n        \"peers\": {\n            \"serverId\" : \"Server1\",\n            \"servers\": {\n                \"Server1\": { \"url\": \"wss://192.168.1.100:443/\" },\n                \"Server2\": { \"url\": \"wss://192.168.1.101:443/\" }\n            }\n        }\n    }\n

The configuration above assumes that server1 has an IP address of \u2018192.168.1.100\u2019 and server2 has \u2018192.168.1.101\u2019 respectively. The \"serverId\" value is a short and unique identifier for each server and it is optional. If it's not specified, the computer hostname is used instead.

The \u201cservers\u201d section of the configuration file should have the identifier of the server followed by each websocket URL and port (generally 443) of the peer servers. If the servers are running with \u201c--tlsoffload\u201d, then use \u201cws://\u201d for the URL instead of \u201cwss://\u201d.

When the MongoDB is setup for the first time, a unique identifier is generated and written into the DB. To prevent situations where two servers with different database from peering together, during peering process, each server will validate among each other if they have the same unique DB identifier. Peering connection will only succeed if this condition is met.

Once peered, all of the servers should act like one single host, no matter which server the user(s) are connected to.

"},{"location":"meshcentral/#email-setup","title":"Email Setup","text":"

We highly recommend the use of an email server (SMTP) because we could allow MeshCentral to verify user account\u2019s email address by sending a confirmation request to the user to complete the account registration and for password recovery, should a user forget account password as illustrated below

A verification email is sent when a new account is created or if the user requests it in the \u201cMy Account\u201d tab.

The password recovery flow when \u201cReset Account\u201d is triggered at the login page.

Both account verification and password recovery are triggered automatically once SMTP mail server configuration is included into the config.json file.

"},{"location":"meshcentral/#smtp-userpass","title":"SMTP: User/Pass","text":""},{"location":"meshcentral/#normal-server","title":"Normal Server","text":"

Update the config.json with \u201csmtp\u201d section as shown below and restart the server.

{\n  \"smtp\": {\n    \"host\": \"smtp.server.com\",\n    \"port\": 25,\n    \"from\": \"myaddress@server.com\",\n    \"user\": \"myaddress@server.com\",      # Optional\n    \"pass\": \"mypassword\",                # Optional\n    \"tls\": false                         # Optional, default false\n  }\n}\n

Please map the host, port values to connect to the right host that provides this SMTP service. For \u201cfrom\u201d value, administrators may put something like donotreply@server.com, but often times it needs to be a valid address since SMTP server will not send out messages with an invalid reply address.

Some SMTP servers will require a valid username and password to login to the mail server. This is to prevent unauthorized e-mail correspondence. TLS option can be set to \u2018true\u2019 if the SMTP server requires TLS.

"},{"location":"meshcentral/#gmail","title":"Gmail","text":"

One option is to configure MeshCentral work with Google Gmail by setting \u201chost\u201d with smtp.gmail.com, and \u201cport\u201d with 587. In the config.json file, use user\u2019s Gmail address for both \u201cfrom\u201d and \u201cuser\u201d and Gmail password in the \u201cpass\u201d value. You will also need to enable \u201cLess secure app access\u201d in for this Google account. It\u2019s in the account settings, security section:

If a Google account is setup with 2-factor authentication, the option to allow less secure applications not be available. Because the Google account password is in the MeshCentral config.json file and that strong authentication can\u2019t be used, it\u2019s preferable to use a dedicated Google account for MeshCentral email.

"},{"location":"meshcentral/#smtp-oauth-authentication","title":"SMTP: OAuth Authentication","text":""},{"location":"meshcentral/#gmail_1","title":"Gmail","text":"

Google has announced that less secure app access will be phased out. For Google Workspace or G-Suite accounts, the following process can be used to allow OAuth2 based authentication with Google's SMTP server. It is likely a very similar process for regular Gmail accounts.

Start by visiting the Google API console:

https://console.developers.google.com/

First, you will create a new project. Name it something unique in case you need to create more in the future. In this example, I've named the project \"MeshCentral\"

Click on the \"OAuth Consent Screen\" link, Under \"APIs and Services\" from the left hand menu:

If you have a Google Workspace account, you will have the option to choose \"Internal\" application and skip the next steps. If not, you will be required to provide Google with information about why you want access, as well as verifying domain ownership.

Add the Gmail address under which you have created this project to the fields labelled \u2018User support email\u2019 and \u2018Developer contact information\u2019 so that you will be allowed for authentication. After that, you will want to add a scope for your app, so that your token is valid for gmail:

Once this is complete, the next step will be to add credentials.

Choose OAuth Client

You will obtain a Client ID and a Client secret once you've completed the process. Be sure to store the secret immediately, as you won't be able to retreive it after you've dismissed the window.

Next, you will need to visit the Google OAuth Playground:

https://developers.google.com/oauthplayground

Enter your Client ID and secret from the last step. On the left side of the page, you should now see a text box that allows you to add your own scopes. Enter https://mail.google.com and click Authorize API.

You will need to follow the instructions provided to finish the authorization process. Once that is complete, you should receive a refresh token. The refresh token, Client ID and Client Secret are the final items we need to complete the SMTP section of our config.json. It should now look something like this:

\"smtp\": {\n    \"host\": \"smtp.gmail.com\",\n    \"port\": 587,\n    \"from\": \"my@googleaccount.com\",\n    \"auth\": {\n      \"clientId\": \"<YOUR-CLIENT-ID>\",\n      \"clientSecret\": \"<YOUR-CLIENT-SECRET>\",\n      \"refreshToken\": \"<YOUR-REFRESH-TOKEN>\"\n    },\n    \"user\": \"noreply@authorizedgooglealias.com\",\n    \"emailDelaySeconds\": 10,\n    \"tls\": false,\n    \"verifyEmail\": true\n  }\n

Regardless of what SMTP account is used, MeshCentral will perform a test connection to make sure the server if working as expected when starting. Hence, the user will be notified if Meshcentral and SMTP server has been configured correctly as shown below.

After successfully configuring the Gmail SMTP server, switch the OAuth 'Publishing Status' from Testing to In Production. This step prevents the need for frequent refresh token generation. Verification of your project isn't required to make this change.

"},{"location":"meshcentral/#database","title":"Database","text":"

A critical component of MeshCentral is the database. The database stores all of the user account information, groups and node data, historical power and event, etc. By default MeshCentral uses NeDB (https://github.com/louischatriot/nedb) that is written entirely in NodeJS and is setup automatically when MeshCentral is installed with the npm tool. The file \u201cmeshcentral.db\u201d will be created in the \u201cmeshcentral-data\u201d folder when MeshCentral is first launched. This database works well for small deployments scenarios.

Besides NeDB, MeshCentral fully supports MongoDB for larger deployments or deployments that require robust reliability or load-balancing. In this section we will see look at how to export and import the database file with a JSON file and how to configure MongoDB.

"},{"location":"meshcentral/#database-export","title":"Database Export","text":"

User could use a practical approach to migrate from NeDB to MongoDB, by exporting the entire content of the existing NeDB into JSON file, setup the new MongoDB and import that JSON file to create the schemas in MongoDB.

To export the database, stop the MeshCentral server and run the server again with \u201c--dbexport\u201d and a JSON file called \u201cmeshcentral.db.json\u201d will be created in the \u201cmeshcentral-data\u201d folder as shown below.

Alternatively, user can also specify the full export path for the JSON file as shown below.

"},{"location":"meshcentral/#database-import","title":"Database Import","text":"

Importing the MeshCentral database is useful when transitioning between database softwares (NeDB to/from MongoDB) or when importing the database from MeshCentral1 via migration tool.

Important

Importing a JSON file will overwrite the entire content of the database. A starting empty database is recommended.

When you are ready to import a JSON file into the database, run meshcentral with \u201c--dbimport\" as shown below. If path is not specified, the application will default to use \u201cmeshcentral.db.json\u201d that is in \u201cmeshcentral-data\u201d folder.

Alternatively, user can specify the full path of the import JSON as shown below.

"},{"location":"meshcentral/#viewing-the-database","title":"Viewing the Database","text":"

For debugging purposes, Meshcentral allow users to have quick preview of certain frequently accessed data in the database with the following options:

Option Description --showusers List of all users in the database. --showmeshes List of all meshes in the database. --shownodes List of all nodes in the database --showevents List all events in the database --showpower List all power events in the database. --showall List all records in the database.

For example, you can show the list of users with the \u201c--showusers\"

"},{"location":"meshcentral/#mongodb-setup","title":"MongoDB Setup","text":"

MongoDB is useful when setting up MeshCentral for two or more peer servers given that all peer servers much have access to the same database. NeDB and MongoDB have similar access interfaces hence the DB migration from one to the other is straight forward. Installing MongoDB depends on its host OS so do check for available download options at mongodb.com. In this guide, we will focus on the 64-bit windows with SSL support installer.

After completing the installation step,

  1. Stop any instance of Meshcentral that is running locally or in any machine
  2. Start a terminal or Windows Command prompt (CMD),
  3. Create a folder \u201cc:\\data\\db\u201d
  4. Go to the MongoDB bin folder and run \u201cmongod --bind 127.0.0.1\u201d.

    This execute the database engine and store the database data in the default location \u201c/data/db\u201d path and bind a loopback on the local port \u201c127.0.0.1\u201d.

    Note: Refer to MongoDB documentation to allow database to run in the background or experiment with alternate configurations.

    Note: Upon successful execution, MongoDB will wait for connections on its default port 27017.

  5. Now run MeshCentral with the command below, it will tell Meshcentral to connect to MongoDB and use \u201cmeshcentral\u201d DB. MongoDB will create this DB if it does not exist.

    node meshcentral --mongodb mongodb://127.0.0.1:27017/meshcentral\n

  6. Alternatively, to transition an existing meshcentral DB from NeDB and to MongoDB, just run the command below:

    node meshcentral --dbexport \nnode meshcentral --mongodb mongodb://127.0.0.1:27017/meshcentral --dbimport\nnode meshcentral --mongodb mongodb://127.0.0.1:27017/meshcentral\n
  7. We recommend the user to include MongoDB configuration into the server\u2019s configuration \u201cconfig.json\u201d to avoid specifying the \u201c--mongodb\" each time MeshCentral is executed as shown below

    {\n  \"settings\": {\n    \"mongodb\": \"mongodb://127.0.0.1:27017/meshcentral\",\n    \"mongodbcol\": \"meshcentral\"\n  }\n}\n

Note: By default, MeshCentral will create a single collections called \u201cmeshcentral\u201d in the specified database. If user want to specify a different collection name, use \u201c--mongodbcol\" or \u201cmongodbcol\u201d for settings like shown above.

If you are using MongoDB with authentication, you can change the URL a little to add the username and password, for example:

mongodb://username:password@127.0.0.1:27017/meshcentral\n

You can also provide extra connection parameters like this:

mongodb://username:password@127.0.0.1:27017/meshcentral?authMechanism=MONGODB-CR&authSource=db\n
"},{"location":"meshcentral/#running-state-less","title":"Running State-less","text":"

By default, MeshCentral will read its configuration information from the \u201cmeshcentral-data\u201d folder. The most important file in that folder being the \u201cconfig.json\u201d file, but the folder also contains certificates, branding images, terms of service and more.

After the configuration is read, MeshCentral will connect to its database and continue to start the server. For most user\u2019s this is a perfectly acceptable way to setup the server. However, in some cases, it\u2019s advantageous to setup the server \u201cstate-less\u201d. That is, there is no local configuration files at all and everything is in the database. Two examples of this would be when running MeshCentral is a Docker container where we don\u2019t want the container to have any state or for compliance with security specifications where the database is \u201cencrypted at rest\u201d. In this cases, we will load the configuration files into the database and MeshCentral will only be told how to connect to the database.

When loading configuration information into the database, MeshCentral requires that a configuration file password be used to encrypt the configuration files in the database. This provides an additional layer of security on top of any authentication and security already provided by the database, if such security has been setup.

To make this happen, we will be using the following command line options from MeshCentral:

Command Description --configkey (key) Specifies the encryption password that will be used to read or write the configuration files to the database. --dblistconfigfiles List the names and size of all configuration files in the database. --dbshowconfigfile (filename) Show the content of a specified filename from the database. --configkey is required. --dbdeleteconfigfiles Delete all configuration files from the database. --dbpushconfigfiles '*' or (folder path) Push a set of configuration files into the database, removing any existing files in the process. When * is specified, the \u201cmeshcentral-data\u201d folder up pushed into the database. --configkey is required. --dbpullconfigfiles (folder path) Get all of the configuration files from the database and place them in the specified folder. Files in the target folder may be overwritten. --configkey is required. --loadconfigfromdb (key) Runs MeshCentral server using the configuration files found in the database. The configkey may be specified with this command or --configkey can be used.

Once we have MeshCentral running as expected using the \u201cmeshcentral-data\u201d folder, we can simply push that configuration into the database and run using the database alone like this:

node ./node_modules/meshcentral --dbpushconfigfiles '*' --configkey mypassword\n\nnode ./node_modules/meshcentral --loadconfigfromdb mypassword --mongodb \"mongodb://127.0.0.1:27017/meshcentral\"\n

This first line will load many of the \u201cmeshcentral-data\u201d files into the database. At this point, we can back up the \u201cmeshcentral-data\u201d folder and remove it. Then run the second line to start the server. Here we use MongoDB, but if one uses NeDB, the \u201cmeshcentral.db\u201d file in the \u201cmeshcentral-data\u201d folder will still be needed.

Note that MeshCentral does not currently support placing a Let\u2019s Encrypt certificate in the database. Generally, one would use a reverse proxy with Let\u2019s Encrypt support and TLS offload in the reverse proxy and then run MeshCentral in state-less mode in a Docket container.

"},{"location":"meshcentral/#commandline-options","title":"Commandline Options","text":"

In general, doing --option value is the same as adding \"option\": value in the settings section of the config.json.

Here are the most common options found by running meshcentral --help

Run as a background service\n   --install/uninstall               Install MeshCentral as a background service.\n   --start/stop/restart              Control MeshCentral background service.\n\nRun standalone, console application\n   --user [username]                 Always login as [username] if account exists.\n   --port [number]                   Web server port number.\n   --redirport [number]              Creates an additional HTTP server to redirect users to the HTTPS server.\n   --exactports                      Server must run with correct ports or exit.\n   --noagentupdate                   Server will not update mesh agent native binaries.\n   --nedbtodb                        Transfer all NeDB records into current database.\n   --listuserids                     Show a list of a user identifiers in the database.\n   --cert [name], (country), (org)   Create a web server certificate with [name] server name.\n                                     country and organization can optionally be set.\n\nServer recovery commands, use only when MeshCentral is offline.\n   --createaccount [userid]          Create a new user account.\n   --resetaccount [userid]           Unlock an account, disable 2FA and set a new account password.\n   --adminaccount [userid]           Promote account to site administrator.\n
"},{"location":"meshcentral/#tls-offloading","title":"TLS Offloading","text":"

A good way for MeshCentral to handle a high traffic is to setup a TLS offload device at front of the server that takes care of doing all the TLS negotiation and encryption so that the server could offload this. There are many vendors who offer TLS or SSL offload as a software module (Nginx* or Apache*) so please contact your network administrator for the best solution that suits your setup.

As shown in the picture below, TLS traffic will come from the Internet and security will be handled by a device ahead of the server and MeshCentral only has to deal with TCP connections.

To make this work, it is important the server is setup with \u201c--tlsoffload\u201d. This indicates the server that TLS is already being taken care of and MeshCentral does not have to deal with it. MeshCentral will continue to listen to port 80, 443 and 4433.

However, incoming port 443 (main web port) and 4433 (Intel\u00ae AMT MPS port) will not have TLS but MeshCentral will still put many HTTPS flags in its responses on port 443. By default, if a user accesses http://127.0.0.1:443 without TLS offloader setting, the browser is expected to display warnings. To make this work, TLS offloader device\u2019s ports and functions should be configured correctly like below

Port Function Description 80 Directly forwards port 80 to MeshCentral port 80 443 Handle TLS using a web certificate and forward to MeshCentral port 443 4433 Handle TLS using MPS certificate and forward to MeshCentral port 4433

If possible, port 443 should be configured with a legitimate trusted certificate and the public part of the certificate named as \u201cwebserver-cert-public.crt\u201d must be placed inside of \u201cmeshcentral-data\u201d folder of the server. When the server is executed in tlsoffload mode, only the public part of the web certificate is used by the server.

For Intel\u00ae AMT MPS port 4433, the certificate files \u201cmpsserver-cert-public.crt\u201d and \u201cmpsserver-cert-public.key\u201d must be copied from the \u201cmeshcentral-data\u201d folder and loaded into the TLS offload module.

Note: Please consult the TLS offloader user manual from the respective vendor to configure TLS offloading feature correctly.

"},{"location":"meshcentral/#lets-encrypt-support","title":"Let\u2019s Encrypt support","text":"

MeshCentral makes use of HTTPS to authenticate and encrypt management traffic over the network. By default, a self-signed certificate is used for the MeshCentral HTTPS server. That certificate is not trusted by browsers and so, you get a warning message when visiting the web site. You can solve this but obtaining a free trusted certificate from Let\u2019s Encrypt (https://letsencrypt.org/). There are some limitations and so, it\u2019s best to get familiar with this service before starting. You will also need a valid domain name that you own and that points to your MeshCentral server.

Before moving forward with this section, make sure your MeshCentral server is working correctly, has a domain name pointing to it and that the HTTP redirection server on port 80 is enabled and working. MeshCentral\u2019s HTTP port 80 server will be used in the process to prove to Let\u2019s Encrypt that we have control over the domain. At any point, you may try to use https://letsdebug.net/ to see if your domain is setup correctly and/or debug any issues. When ready, add the \u201cletsencrypt\u201d section to the config.json file like this:

{\n  \"settings\": {\n    \"RedirPort\": 80,\n  },\n  \"letsencrypt\": {\n    \"email\": \"myemail@myserver.com\",\n    \"names\": \"domain1.com,domain2.com\",\n    \"rsaKeySize\": 3072,\n    \"production\": false\n  },\n}\n

The only mandatory field is the email address, please enter a valid one.

The names section is a list of domain names the requested certificate will be valid for. This must be a list of DNS names that are already pointing to your server. It\u2019s important to understand you are not requesting these DNS names, rather, Let\u2019s Encrypt will makes requests to prove control over all of these domain name before issuing the certificate. All the domain names you enter must point to the server and HTTP port 80 must be reachable over the internet. If you don\u2019t specify names, the default MeshCentral certificate name is used, that is the configured \u201c--cert [name]\u201d.

The RSA key size can only be 2048 or 3072, with the default being 3072. This is the number of bit used for the RSA key in the certificate. Bigger is more secure, but takes more time to compute.

Lastly the production key, by default this is false. When set to false, MeshCentral will query the Let\u2019s Encrypt staging server for a certificate. It\u2019s highly recommended to try this first and make sure everything works before getting a real certificate. Keep production to false, run thru the process at least once and make sure everything works. You will get a new certificate installed on the HTTPS server signed by a staging Let\u2019s Encrypt certificate authority.

The Let\u2019s Encrypt certificates and files will be created in the \u201cmeshcentral-data\u201d folder. Make sure to keep regular backups of the \u201cmeshcentral-data\u201d folder and all sub-folders.

Once you placed the \u201cletsencrypt\u201d section in config.json, restart the server. The request to the Let\u2019s Encrypt server may take a few minutes to a few hours. It\u2019s best to have your DNS server name pointing to your server for over a day before doing this. Once the new certificate is received, the server will automatically restart and browsing to HTTPS on your server will show the new certificate. Here is what it looks like on FireFox:

If you successfully setup a Let\u2019s Encrypt certificate using the Let\u2019s Encrypt staging server (\u201cproduction\u201d: false) and everything looks good, stop the server, remove the \u201cletsencrypt\u201d folder in \u201cmeshcentral-data\u201d, change production to \u201ctrue\u201d and start the server again. You should get a real certificate in a few minutes to a few hours. MeshCentral will automatically renew the certificate a few days before it expires. The MeshCentral self-signed certificate will still be present in the \u201cmeshcentral-data\u201d folder, this is normal and there is no need to manually copy the Let\u2019s Encrypt certificate to the \u201cmeshcentral-data\u201d folder. If something goes wrong with the Let\u2019s Encrypt certificate, the server will fall back to using the self-signed one.

Note

Please be patient with Let\u2019s Encrypt certificate requests and make sure you correctly get a staging certificate before setting production to true.

If Let\u2019s Encrypt works for you, please consider donating to them as they provide a critical service to the Internet community.

"},{"location":"meshcentral/#server-ip-filtering","title":"Server IP filtering","text":"

For improved security, it\u2019s good to limit access to MeshCentral with IP address. For example, we want to allow mesh agents and Intel AMT computers to connect from anywhere, but whitelist IP address for users that we allow to access MeshCentral.

MeshCentral provides IP filtering option in the config.json file for each domain. For an example, we can set IP address whitelist for the default domain like as shown below.

    {\n        \"domains\": {\n            \"\": {\n                \"userallowedip\" : \"1.2.3.4,1.2.3.5\",\n            }\n        }\n    }\n

IP addresses are separated by a comma. As a result, only users coming these IP addresses will be able to see the server\u2019s login page as illustrated below. Other IP addresses will be blocked effectively.

Note: When IP address whitelist is effective, Mesh Agent connection from any IP address will be not affected.

You can also use files for IP lists

\"userAllowedIp\": \"file:userallowedips.txt\",\n\"userBlockedIp\": \"file:userblockedips.txt\",\n\"agentAllowedIp\": \"file:agentallowedips.txt\"\n

Place the file in the meshcentral-data folder.

All the lines that start with a number or : will be used, everything else is ignored. So, you can put comments anyway you like, but probably best to start then with a # or something to make it clear.

# My list of blocked IP's\n185.101.70.0/24\n185.46.85.0/24\n37.9.44.0/24\n37.9.45.0/24\n5.189.205.0/24\n5.189.206.0/24\n5.189.207.0/24\n5.62.153.0/24\n5.62.156.0/24\n5.62.158.0/24\n\n# One more list\n5.8.44.0/24\n5.8.45.0/24\n5.8.46.0/24\n79.110.28.0/24\n79.110.31.0/24\n91.204.14.0/24\n95.181.218.0/24\n95.85.81.0/24\n
"},{"location":"meshcentral/#embedding-meshcentral","title":"Embedding MeshCentral","text":"

One interesting way to use MeshCentral is to embed its features into another web site. In other words, certain feature of MeshCentral can be selectively embedded into another website such as Remote Desktop or File Transfer.

This allows another site to take care of the user accounts and business processes while MeshCentral takes care of remote management. In the example below, a user logs into an existing web site and received a page with MeshCentral remote desktop embedded into it.

To make this work, a following key alignment is required: 1. When a user requests the business website, the business web server must return the user a web page containing an iframe with a URL that points to the MeshCentral server. 2. The URL must contain both a login token and embedding options. The login token tells MeshCentral under what MeshCentral account this request should be made. 3. The login token replaces the login screen of MeshCentral. Then, the embedding options can be used to specify no page title, header and footer to be displayed. This way, the page given by MeshCentral will fit nicely into the iframe.

In this section we will review both the login token and embedding options mentioned above.

"},{"location":"meshcentral/#login-token","title":"Login Token","text":"

With MeshCentral, it\u2019s possible to login to the main web page without even seeing the login screen. Of course, you can do this by specifying \u201c--nousers\" or \u201c--user admin\u201d when you run the server, but these approach are not secure as it removes user authentication for those accessing the server.

With login tokens feature, a token can be generated to be used for a short time to login and skip the login page. This is perfect for embedding MeshCentral usages into other web site and probably for other applications.

To enable this feature, configure config.json file to allow login tokens.

{\n  \"settings\": {\n    \"allowLoginToken\": true,\n    \"allowFraming\": true\n  }\n}\n

Set both allowLoginToken and allowFraming to \u2018true\u2019 to use login tokens along with framing MeshCentral within another web page.

Next, create a token. Execute MeshCentral with the \u201c--logintoken [userid]\u201d switch and userid value with the example below:

The \u201cuserid\u201d is actually a combination of three values - user, domain, and username in a single string \u201cuser/domain/username\u201d. The example above is using a default domain which is empty hence, the userid will be just \u201cuser//admin\u201d to request for login token. Domains are only used if the server in multi-tenancy mode as discussed in previous chapters.

The resulting hashed base64 encoded blob can be used as a login token for 1 hour. Simply add the \u201c?login=\u201d followed by the token value generated to the URL of the webserver. For an e.g. https://localhost/?login=23tY7@wNbPoPLDeXVMRmTKKrqVEJ3OkJ. The login page is expected to be skipped and automatically login the user admin. This is just a manual attempt to token based login.

Now, to have this work seamlessly with a different website, we should generate a login token key. A token key can be used to generate login tokens whenever needed for MeshCentral. Generate this key with \u201c--loginTokenKey\" switch as shown below

The generated masker key must be placed in a secure location within the business website.

As illustrated above, we see the business site using the token key to generate a login token and embed it into the response web page. The user\u2019s browser then loads the iframe that includes both the URL with the login token for MeshCentral. MeshCentral can then verify the token and allow the web page to load as expected.

"},{"location":"meshcentral/#embedding-options","title":"Embedding Options","text":"

There are multiple options available for user to explicitly choose the features that will be loaded from MeshCentral to the business website. The argument in the in the URL can dictate which web page should display and how. The three embedding URL arguments are Viewmode, Hide and Node.

Embedding Options / URL Argument Description \"ValuesNote: For values 10 and above, a node identifier must be specified.\" viewmode \"Indicates the information to show. This is an integer value, possible values are:\" \"1 = Devices tab2 = Account tab3 = Events tab4 = Users tab (Site admins only)5 = Server files tab10 = Device general information11 = Device remote desktop12 = Device terminal14 = Device Intel AMT console.15 = Device Mesh Agent console hide \"Indicates which portion of the web page to hide. This is a bitmask integer hence it will need the sum of values. For .e.g.: To hide all of the values, add 1+2+4+8 and use 15 as the value. 1 = Hide the page header2 = Hide the page tab4 = Hide the page footer8 = Hide the page title16 = Hide the left tool bar32 = Hide back buttons node Optional unless Viewmode is set to value of 10 or greater. Indicates which node to show on the screen,For example, if we want to embed the remote desktop page for a given node and hide the header, tabs, footer and page title, we could have this URL: https://localhost/?node=UkSNlz7t...2Sve6Srl6FltDd&viewmode=11&hide=15\" Node or NodeID is a long base64 encoded SHA384 value

Note: Typically, the URL for the website is followed by \u201c?\u201d then a set of name=value pairs separated by \u201c&\u201d.

Based on the URL https://localhost/?node=UkSNlz7t...2Sve6Srl6FltDd&viewmode=11&hide=15 , the nodeID starts with \u201cUkSNlz7t\u201d. We shortened the value in this example, but it\u2019s normally a long base64 encoded SHA384 value. The Viewmode set to 11 which is the remote desktop page and Hide set to 15 to hide everything. Hence the user may see as illustrated below.

Only the remote desktop viewer will be displayed embedded within an iframe.

Note: User must set \u201callowFraming\u201d to true in the config.json of the server. This is in addition to the Node, Viewmode and Hide arguments, the login token must be specified to add complex features into another website.

"},{"location":"meshcentral/#server-port-aliasing","title":"Server port aliasing","text":"

In some cases, you may be setting up a server on a private network that uses non-standard ports, but use a router or firewall in front to perform port mapping. So, even if the server privately uses non-standard ports, the public ports are the standard ports 80 and 443. You have to tell MeshCentral to bind to private ports but pretend it\u2019s using the other standard ports when communicating publicly. To make this work, MeshCentral supports port aliasing.

For example you can run:

    node meshcentral --redirport 2001 --port 2002 --aliasport 443\n

Here, the server binds the HTTP and HTTPS ports to 2001 and 2002, but the server will externally indicate to MeshAgents and browsers that they must connect to port 443.

In a different situation, you may want to setup a server so that both Mesh Agents and Intel AMT connect back to the server on port 443. This is useful because some corporation have firewalls that restrict outgoing connections to only port 80 and 443. By default, MeshCentral will be setup to have MeshAgents connection on port 443 and Intel AMT on port 4433.

In the following picture we have a usual server running with:

node meshcentral --cert Server1 --port 443 --mpsport 4433\n

We can setup the server so that MeshAgent and Intel AMT will connect on port 443 of two different IP address or names like this:

node meshcentral --cert Server1 --mpscert Server2\n--port 443 --mpsport 4433 --mpsaliasport 443\n

In the second example, the server on the right is running HTTPS on port 443 and MPS on port 4433 as usual, but the MPS is now presenting a certificate that has the name \u201cServer2\u201d on it. The server will also configure Intel AMT CIRA to connect to \u201cServer2:443\u201d.

A router or firewall that is located in front of the MeshCentral server needs to be configured correctly to forwarding:

Server1:443 -> 443 on MeshCentral\nServer2:443 -> 4433 on MeshCentral\n

The routing of IP and ports by the firewall shown on the picture must be configured separately from MeshCentral using separate software. Typically, routers or firewalls have the proper controls to configure this type of traffic routes.

"},{"location":"meshcentral/#web-relay-using-dns-names-and-multiple-web-relays","title":"Web relay using DNS names and multiple web relays","text":"

MeshCentral has a web relay feature that allows a user to access remote web sites thru the MeshCentral server without having to install MeshCentral Router. Web relay also allow you to use an alternate DNS name instead of a different web relay port which has a few advantages. You can also use multiple alternate DNS names which can be used at the same time to provide users with many HTTP/HTTPS relays.

"},{"location":"meshcentral/#video-walkthrus","title":"Video Walkthrus","text":""},{"location":"meshcentral/#device-groups-with-relay-agent","title":"Device Groups with Relay Agent","text":"

MeshCentral supports the local device group allowing devices that do not have an agent to be managed thru MeshCentral with regular SSH, SFTP, RDP, VNC protocols. Until now, the MeshCentral server had to be in LAN or Hybrid modes to support his device group and the managed devices had to be on the same network as the MeshCentral server. Starting with v1.0.11, users can create a local device group specifying a MeshAgent as a relay. This makes it possible to manage agent-less devices from anywhere on the Internet even if the server is in WAN mode. Simply install a single device with a MeshAgent on a network and create a local device group with that device as the relay.

To enable SSH support, add this line to the domain section of your config.json:

\"ssh\": true\n

Video Walkthru

"},{"location":"meshcentral/#raritan-and-webpowerswitch-with-relay","title":"Raritan and WebPowerSwitch with Relay","text":"

In addition to local device groups, the IP-KVM/Power switch device group was also improved to support a MeshAgent as a relay. This is big news for Raritan IP-KVM switch owners as you can now monitor your IP-KVM ports and access them remotely from the Internet. The same can be done with WebPowerSwitch allowing full out-of-band remote access to devices from anywhere in the world.

"},{"location":"meshcentral/#nginx-reverse-proxy-setup","title":"NGINX Reverse-Proxy Setup","text":""},{"location":"meshcentral/#video-walkthru_1","title":"Video Walkthru","text":"

Sometimes it\u2019s useful to setup MeshCentral with a reverse-proxy in front of it. This is useful if you need to host many services on a single public IP address, if you want to offload TLS and perform extra web caching. In this section we will setup NGINX, a popular reverse-proxy, in front of MeshCentral. NGNIX is available at: https://www.nginx.com/

In this example, we will:

  • MeshCentral on non-standard ports, but alias HTTPS to port 443.
  • NGINX will be using standard ports 80 and 443.
  • We will have NGINX perform all TLS authentication & encryption.
  • MeshCentral will read the NGINX web certificate so agents will perform correct server authentication.
  • NGINX will be setup with long timeouts, because agents have long standard web socket connections.

Note

With SELinux, NGINX reverse proxy requires 'setsebool -P httpd_can_network_relay 1' Caution: httpd_can_network_relay only allows certain ports Confirm you are using ports from this subset in MeshCentral If you want to use a different port then you will need to add it to http_port_t

Let\u2019s get started by configuring MeshCentral with the following values in config.json:

{\n  \"settings\": {\n    \"Cert\": \"myservername.domain.com\",\n    \"Port\": 4430,\n    \"AliasPort\": 443,\n    \"RedirPort\": 800,\n    \"AgentPong\": 300,\n    \"TlsOffload\": \"127.0.0.1\"\n  },\n  \"domains\": {\n    \"\": {\n      \"certUrl\": \"https://127.0.0.1:443/\"\n    }\n  }\n}\n

With this configuration, MeshCentral will be using port 4430 instead of port 443, but because \u201cTlsOffload\u201d is set, TLS will not be performed on port 4430. The server name is set to \u201cmyservername.domain.com\u201d, so that is the name that MeshCentral will give to agents to connect to. Also, the alias port is set to 443. So agents will be told to connect to \u201cmyservername.domain.com:443\u201d.

The \u201cAgentPong\u201d line instructs the server to send data to the agent each 300 seconds and the agent by default will send data to the server every 120 seconds. As long as NGINX timeouts are longer than this, connections should remain open.

When agents connect, they will see the NGINX TLS certificate on port 443. MeshCentral needs to know about the NGINX certificate so that it can tell the agents this is the correct certificate they should expect to see. So, \u201ccertUrl\u201d is used to tell MeshCentral where to get the certificates that agents will see when connecting.

When NGINX forwards connections to MeshCentral, extra X-Forwarded headers will be added to each request. MeshCentral needs to know if these headers can be trusted or not. By setting \u201cTlsOffload\u201d to \u201c127.0.0.1\u201d, MeshCentral is told to trust these headers when requests come from \u201c127.0.0.1\u201d.

In this example, make sure to change \u201c127.0.0.1\u201d to the IP address of NGINX and \u201cCert\u201d to the external DNS name of the NGINX server.

Next, we need to configure and launch NGINX. Here is an ngnix.conf to get started:

worker_processes 1;\n\nevents {\n    worker_connections 1024;\n}\n\nhttp {\n    # HTTP server. In this example, we use a wildcard as server name.\n    server {\n        listen 80;\n        server_name _;\n\n        location / {\n            proxy_pass http://127.0.0.1:800/;\n            proxy_http_version 1.1;\n\n            # Inform MeshCentral about the real host, port and protocol\n            proxy_set_header X-Forwarded-Host $host:$server_port;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_set_header X-Forwarded-Proto $scheme;\n        }\n    }\n\n    # HTTPS server. In this example, we use a wildcard as server name.\n    server {\n        listen 443 ssl;\n        server_name _;\n\n        # MeshCentral uses long standing web socket connections, set longer timeouts.\n        proxy_send_timeout 330s;\n        proxy_read_timeout 330s;\n\n        # We can use the MeshCentral generated certificate & key\n        ssl_certificate webserver-cert-public.crt;\n        ssl_certificate_key webserver-cert-private.key;\n        ssl_session_cache shared:WEBSSL:10m;\n        ssl_ciphers HIGH:!aNULL:!MD5;\n        ssl_prefer_server_ciphers on;\n\n        location / {\n            proxy_pass http://127.0.0.1:4430/;\n            proxy_http_version 1.1;\n\n            # Allows websockets over HTTPS.\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection \"upgrade\";\n            proxy_set_header Host $host;\n\n            # Inform MeshCentral about the real host, port and protocol\n            proxy_set_header X-Forwarded-Host $host:$server_port;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_set_header X-Forwarded-Proto $scheme;\n        }\n    }\n}\n

As indicated in the comments of this NGINX configuration file, we set timeouts to be really long. We forward HTTP port 80 and HTTPS port 443 to the corresponding ports on MeshCentral. In this example, we happen to use the web certificates that where generated by MeshCentral, but any certificate is ok. We also add extra \u201cX-Forward\u201d headers, this tells MeshCentral information that would normally be hidden by NGINX, like the client\u2019s IP address and more.

Now we are ready to start NGINX and MeshCentral. You should start NGINX first because MeshCentral will try to fetch the certificate from NGINX upon start. When starting MeshCentral, you should see something like this:

MeshCentral HTTP redirection web server running on port 800.\nLoaded RSA web certificate at https://127.0.0.1:443/, SHA384: d9de9e27a229b5355708a3672fb23237cc994a680b3570d242a91e36b4ae5bc96539e59746e2b71eef3dbdabbf2ae138.\nMeshCentral Intel(R) AMT server running on myservername.domain.com:4433.\nMeshCentral HTTP web server running on port 4430, alias port 443.\n

Notice on the second line, MeshCentral will have loaded the web certificate from NGNIX and display a matching hash. That is it, navigating to port 80 and 443 on NGINX should show the MeshCentral web page and agents should connect as expected.

"},{"location":"meshcentral/#cira-setup-with-nginx","title":"CIRA Setup with NGINX","text":"

We can add on the section above and support reverse proxy for Intel\u00ae AMT Client Initiated more Access (CIRA) connecting that come to the server. Normally, CIRA connections come on port 4433 and use TLS.

Since CIRA is a binary protocol, care must be taken to configure NGINX to handle the data as a TCP stream instead of HTTP. At the very bottom of the nginx.conf file, we can add the following:

stream {\n    # Internal MPS servers, in this case we use one MeshCentral MPS server is on our own computer.\n    upstream mpsservers {\n        server 127.0.0.1:44330 max_fails=3 fail_timeout=30s;\n    }\n\n    # We can use the MeshCentral generated MPS certificate & key\n    ssl_certificate mpsserver-cert-public.crt;\n    ssl_certificate_key mpsserver-cert-private.key;\n    ssl_session_cache shared:MPSSSL:10m;\n    ssl_ciphers HIGH:!aNULL:!MD5;\n    ssl_prefer_server_ciphers on;\n\n    # MPS server.\n    server {\n        listen 4433 ssl;\n        proxy_pass mpsservers;\n        proxy_next_upstream on;\n    }\n}\n\nNGINX will listen on port 4433, decrypt the connection and forward it to 44330 on the loopback interface. We are going to be used the \u201cmpsserver\u201d certificate that was created by MeshCentral as the TLS server certificate for port 4433. Now, we just have to make a few changes to the MeshCentral config.json file.\n\n{\n  \"settings\": {\n    \"Cert\": \"myservername.domain.com\"\n    \"Port\": 4430,\n    \"AliasPort\": 443,\n    \"RedirPort\": 800,\n    \"TlsOffload\": \"127.0.0.1\"\n    \"MpsPort\": 44330,\n    \"MpsAliasPort\": 4433,\n    \"MpsTlsOffload\": true\n  },\n  \"domains\": {\n    \"\": {\n      \"certUrl\": \"https://127.0.0.1:443/\"\n    }\n  }\n}\n

In this new config.json, we added 3 lines. First, the MeshCentral Management Presence Server (MPS) is now on port 44330. However, the MpsAliasPort value indicates that externally, port 4433 will be used, so we need to configure Intel AMT to connect to port 4433. Lastly, we want to disable TLS support on port 44330 by setting \u201cMpsTlsOffload\u201d to true.

With this configuration, Intel AMT CIRA connections will come in and TLS will be handled by NGINX. With this setup, it\u2019s not possible to configure Intel AMT CIRA to connect using mutual-TLS authentication, only username/password authentication is used.

"},{"location":"meshcentral/#traefik-reverse-proxy-setup","title":"Traefik Reverse-Proxy Setup","text":"

In this section, we will setup MeshCentral with Traefik, a popular reverse proxy software. This section will be much like the previous section setting up NGNIX but with a different software and configuration file. Traefik is open source and available at: https://traefik.io/

This section covers a really simple Traefik configuration. Traefik is capable of a lot more complex configurations.

In this example, we will:

  • MeshCentral on non-standard ports, but alias HTTPS to port 443.
  • Traefik will be using standard ports 80 and 443.
  • We will have Traefik perform all TLS authentication & encryption.
  • MeshCentral will read the NGINX web certificate so agents will perform correct server authentication.

First we will start with the MeshCentral configuration, here is a minimal configuration that will work:

{\n  \"settings\": {\n    \"Cert\": \"myservername.domain.com\",\n    \"Port\": 4430,\n    \"AliasPort\": 443,\n    \"RedirPort\": 800,\n    \"TlsOffload\": \"127.0.0.1\"\n  },\n  \"domains\": {\n    \"\": {\n      \"certUrl\": \"https://127.0.0.1:443/\"\n    }\n  }\n}\n

Note the \u201cagentConfig\u201d line: Because Traefik does not support web socket connections that are not \u201cmasked\u201d, we have to tell the Mesh Agents to mask web socket connections using this line. Once set, any new agent will be installed with the web socket masking turned on. Also note that we will be running MeshCentral on port HTTPS/4430 and HTTP/800. However, we also indicate to MeshCentral that HTTPS will really be on port 443 using the \u201cAliasPort\u201d line.

The \u201cTlsOffload\u201d line indicates that MeshCentral should not perform TLS on port 4430. And the \u201ccertUrl\u201d line indicates what URL can be used to load the external certificate that will be presented on port 443 in front of MeshCentral.

Now that we have MeshCentral setup, let\u2019s take a look at a sample Traefik configuration file. In this case, we will manually configure the entrypoints, frontends and backends within the Traefik configuration file. There is a basic configuration file for Traefik 1.7:

[global]\n  checkNewVersion = false\n  sendAnonymousUsage = false\n\n[entryPoints]\n  [entryPoints.http]\n  address = \":80\"\n    [entryPoints.http.redirect]\n    entryPoint = \"https\"\n  [entryPoints.https]\n  address = \":443\"\n    [entryPoints.https.tls]\n      [[entryPoints.https.tls.certificates]]\n      certFile = \"webserver-cert-public.crt\"\n      keyFile = \"webserver-cert-private.key\"\n\n[file]\n\n[backends]\n  [backends.backend1]\n    [backends.backend1.healthcheck]\n      path = \"/health.ashx\"\n      interval = \"30s\"\n\n    [backends.backend1.servers.server1]\n    url = \"http://127.0.0.1:4430\"\n    weight = 1\n\n[frontends]\n  [frontends.frontend1]\n  entryPoints = [\"https\"]\n  backend = \"backend1\"\n  passHostHeader = true\n  [frontends.frontend1.routes]\n    [frontends.frontend1.routes.main]\n    rule = \"Host:myserver.domain.com,localhost\"\n\n[api]\n  entryPoint = \"traefik\"\n  dashboard = true\n

The enterPoints section shows we have two entry points, port 80 will be redirected to port 443. Traefik will perform this redirection so MeshCentral will never see port 80 connections. Port 443 will be setup using the given TLS certificates. In this example, we just used the certificate files generated by MeshCentral in the \u201cmeshcentral-data\u201d folder. You can use the two certificate files as-is.

The backends section configures one MeshCentral server on port \u201c4430\u201d. Traefik will additionally check the health of the MeshCentral server periodically, every 30 seconds.

The frontends section is what routes the connections coming in the entry points to the backend servers. In this case, the HTTPS entry point is routed to the MeshCentral server is the hostname matches \u201cmyserver.domain.com\u201d or \u201clocalhost\u201d.

Finally, the API section creates a web portal on port 8080 for monitoring of Traefik.

"},{"location":"meshcentral/#haproxy-reverse-proxy-setup","title":"HAProxy Reverse-Proxy Setup","text":"

In this section, we will setup MeshCentral with HAProxy, a small popular reverse proxy software. This section will be much like the previous sections setting up NGNIX and Traefik but with a different software and configuration file. HAProxy is free and available at: https://www.haproxy.org/

This section covers a really simple configuration. HAProxy is capable of a lot more complex configurations. In the following example, HAProxy will perform TLS and forward the un-encrypted traffic to MeshCentral on port 444. HAProxy will add extra \u201cX-Forwarded-Host\u201d headers to the HTTP headers so that MeshCentral will know from the IP address the connection comes from.

In the following configuration file, we have browser connections on port 80 being redirected to HTTPS port 443. We also have Let\u2019s Encrypt cert bot for getting a real TLS certificate and \u201cmesh.sample.com\u201d being redirected to 127.0.0.1:444.

global\n    log /dev/log local0\n    log /dev/log local1 notice\n    chroot /var/lib/haproxy\n    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners\n    stats timeout 30s\n    user haproxy\n    group haproxy\n    daemon\n\ndefaults\n    log global\n    mode http\n    option httplog\n    option dontlognull\n    option forwardfor\n    option http-server-close\n\nfrontend http\n    bind *:80\n    redirect scheme https code 301 if !{ ssl_fc }\n\nfrontend https\n    bind *:443 ssl crt /etc/haproxy/cert.pem\n    http-request add-header X-Forwarded-Proto https\n    acl acmepath path_beg /.well-known/acme-challenge/\n    acl meshcentralhost hdr(host) -i mesh.sample.com\n    acl meshcentralhost hdr(host) -i mesh.sample.com:443\n    use_backend acme if acmepath\n    use_backend meshcentral if meshcentralhost\n\nbackend acme\n    server certbot localhost:54321\n\nbackend meshcentral\n    http-request add-header X-Forwarded-Host %[req.hdr(Host)]\n    server meshcentral 127.0.0.1:444\n

On the MeshCentral side, we are not going to use port 80 and need the main HTTPS port to not perform TLS and listen on port 444.

{\n  \"settings\": {\n    \"Cert\": \"myservername.domain.com\",\n    \"Port\": 444,\n    \"AliasPort\": 443,\n    \"RedirPort\": 0,\n    \"TlsOffload\": \"127.0.0.1\"\n  },\n  \"domains\": {\n    \"\": {\n      \"certUrl\": \"https://127.0.0.1:443/\"\n    }\n  }\n}\n

We also specify \u201c127.0.0.1\u201d in TLS offload since we want MeshCentral to make use of the X-Forwarded-Host header that is set by HAProxy.

"},{"location":"meshcentral/#running-in-a-production-environment","title":"Running in a Production Environment","text":"

When running MeshCentral is a production environment, administrators should set NodeJS to run in production mode. There is a good article here (http://www.hacksparrow.com/running-express-js-in-production-mode.html) on what this mode is and how to set it. This mode will also boost the speed of the web site on small devices like the Raspberry Pi. To run in production mode, the environment variable \u201cNODE_ENV\u201d must be set to \u201cproduction\u201d. On Linux, this is done like this:

export NODE_ENV=production\n

On Windows, it\u2019s done like this:

SET NODE_ENV=production\n

Special care must be taken to set the environment variable in such a way that if the server is rebooted, this value is still set. Once set, if you run MeshCentral manually, you will see:

MeshCentral HTTP redirection web server running on port 80.\nMeshCentral v0.2.2-u, Hybrid (LAN + WAN) mode, Production mode.\nMeshCentral Intel(R) AMT server running on devbox.mesh.meshcentral.com:4433.\nMeshCentral HTTPS web server running on devbox.mesh.meshcentral.com:443.\n

In production mode, ExpressJS will cache some files in memory making the web server much faster and any exceptions thrown by the ExpressJS will not result in the stack trace being sent to the browser.

"},{"location":"meshcentral/#two-step-authentication","title":"Two step authentication","text":"

If the MeshCentral server is setup with a certificate name and not setup to use Windows domain authentication, then users will have the options to use 2-step authentication using the Google Authenticator application or any compatible application. Use of this option should be encouraged for users that manage a lot of critical computers. Once active the users will need to enter their username, password and a time limited token to login.

To get this features setup, users will need to go to the \u201cMy Account\u201d tab or the \u201cMy Account\u201d menu in the mobile application. They then select, \u201cAdd 2-stop login\u201d and follow the instructions.

Note that if a user performs a password recovery using email, the 2-step authentication is then turned off and will need to be turned on again. This is not idea as someone being able to intercept the user\u2019s email could still log into the web site. Users should make sure to properly protect their email account.

Another form of MFA or Multi-factor Authentication is hardware based OTP (One Time Password) solution providing 2FA or Two-factor authentication. Yubikey is fully supported in MeshCentral.

And taking authentication to the next step is removing the login page entirely. Use LoginKey 3FA with MeshCentral.

"},{"location":"meshcentral/#server-backup-restore","title":"Server Backup & Restore","text":"

It\u2019s very important that the server be backed up regularly and that a backup be kept offsite. Luckily, performing a full backup of the MeshCentral server is generally easy to do. For all installations make sure to back up the following two folders and all sub-folders.

meshcentral-data\nmeshcentral-files\n

If using NeDB that is built into MeshCentral, you are done. If you are running MongoDB, you will need to perform an extra step. In the command shell, run mongodump to archive all of the MongoDB databases.

mongodump --archive=backup.archive\n

Then, keep the backup.archive file in a safe place. It\u2019s critical that the content of meshcentral-data be backed up in a secure location and preferably using encryption, this is because it contains certificates that give this server its unique personality. Once agents are installed, they will only connect to this server and no other. If you reinstall MeshCentral, even if it is with the same domain name, agents will not connect to the new server since the server certificates are different. Also, someone with access to a backup of \u201cmeshcentral-data\u201d could impersonate the server.

To restore back backup, just install a MeshCentral server, make sure it works correctly. Stop it, wipe the old \u201cmeshcentral-data\u201d and \u201cmeshcentral-files\u201d and put the backup version instead. If using MongoDB, copy the backup.archive back, make sure to clean up any existing \u201cmeshcentral\u201d database, run \u201cmongo\u201d and type:

use meshcentral\ndb.dropDatabase()\n

Then exit with Ctrl-C and run:

mongorestore --archive=backup.archive\n

This will re-import the database from the backup. You can then start MeshCentral again.

Note

The two values for backup and restore in the json are only valid for databases backed by NeDB

"},{"location":"meshcentral/#backup-to-google-drive","title":"Backup to Google Drive","text":"
sudo systemctl stop meshcentral.service\nnano /opt/meshcentral/meshcentral-data/config.json\n

Remove underscored items

sudo systemctl start meshcentral.service\nsudo systemctl status meshcentral.service\n

Log into your MC:

Create desktop app

Enter the Client ID and Client Secret into MC

"},{"location":"meshcentral/#hashicorp-vault-support","title":"HashiCorp Vault support","text":"

MeshCentral has built-in support for HashiCorp Vault so that all configuration and certificates used by MeshCentral are retrieved from a Vault server. Vault is a secret store server and when used with MeshCentral, the MeshCentral server will not be storing any secrets locally. You can get started with Vault here: https://www.vaultproject.io/

Once you got a MeshCentral server working correctly, you can start a simple demonstration Vault server by typing:

vault server -dev\n

When you run the server in developer mode, you will see a secret token and unseal key on the screen. These two values will be used in the commands to follow. You can load the configuration file and all certificates from \u201cmeshcentral-data\u201d into Vault by typing this:

node node_modules/meshcentral --vaultpushconfigfiles --vault http://127.0.0.1:8200 --token s.cO4\u2026 --unsealkey 7g4w\u2026 --name meshcentral\n

Once all of the files have been written into Vault, you can take a look at the Vault web user interface to see all of the secrets. It will be in \u201csecret/meshcentral\u201d:

The \u201cconfig.json\u201d and \u201cterms.txt\u201d files and files in \u201cmeshcentral-data\u201d that end with \u201c.key\u201d, \u201c.crt\u201d, \u201c.jpg\u201d and \u201c.png\u201d will be stored in Vault. You can then run MeshCentral like this:

node node_modules/meshcentral --vault http://127.0.0.1:8200 --token s.cO4\u2026 --unsealkey 7g4w\u2026 --name meshcentral\n

MeshCentral will first read all of the files from Vault and get started. An alternative to this is to create a very small config.json file in \u201cmeshcentral-data\u201d that contains only the Vault configuration like this:

{\n  \"settings\": {\n    \"vault\": {\n      \"endpoint\": \"http://127.0.0.1:8200\",\n      \"token\": \"s.cO4Q\u2026\",\n      \"unsealkey\": \"7g4wFC\u2026\",\n      \"name\": \"meshcentral\"\n    }\n  }\n}\n

Once the config.json file is setup, you can just run MeshCentral without any arguments.

node node_modules/meshcentral\n

Lastly you can all pull all of the files out of Vault using this command line:

node node_modules/meshcentral --vaultpullconfigfiles --vault http://127.0.0.1:8200 --token s.cO4\u2026 --unsealkey 7g4w\u2026 --name meshcentral\n

And delete the Vault secrets using this:

node node_modules/meshcentral --vaultdeleteconfigfiles --vault http://127.0.0.1:8200 --token s.cO4\u2026 --unsealkey 7g4w\u2026 --name meshcentral\n
"},{"location":"meshcentral/#database-record-encryption","title":"Database Record Encryption","text":"

Regardless if using the default NeDB database or MongoDB, MeshCentral can optionally encrypt sensitive data that is stored in the database. When enabled, this encryption is applied to user credentials and Intel AMT credentials.

The additional encryption does the affect database operations and can be used in addition to additional database security. In the following image, we see on the left a normal user record including user credential hashes and data required for two-factor authentication. On the right side, these values are encrypted using AES-256-GCM in the \u201c_CRYPT\u201d field.

Only some data fields are encrypted and the \u201c_CRYPT\u201d entry will only be present when one or more fields are present that need to be secured. To enable this feature, add the \u201cDbRecordsEncryptKey\u201d with a password string to the \u201csettings\u201d section of the config.json like this:

{\n  \"settings\": {\n    \"Port\": 4430,\n    \"RedirPort\": 800,\n    \"DbRecordsEncryptKey\": \"MyReallySecretPassword\"\n  }\n}\n

The provided password will be hashed using SHA384 and the result with be used as an encryption key. When DbRecordsEncryptKey is set, any new or updated records that are written will be encrypted when needed. Existing encrypted records will be read and decrypted as needed. You can force the all entries to be re-written by running:

node node_modules/meshcentral --recordencryptionrecode\n

This command will re-write entries in the database that could require added security and force the application of record encryption. You can also specify a key for decryption only like this:

{\n  \"settings\": {\n    \"Port\": 4430,\n    \"RedirPort\": 800,\n    \"DbRecordsDecryptKey\": \"MyReallySecretPassword\"\n  }\n}\n

When set, the key will only be used for decryption and any new or updated records in the database will not be written with record encryption. You can then run this command again to force all records to be rewritten without encryption:

node node_modules/meshcentral --recordencryptionrecode\n

It\u2019s really important to keep the encryption key in a safe place along with database backups. If the database is backed up but the record encryption key is lost, it will not be possible to recover the secured data in the database.

Also note that database record encryption can and should be used along with other data protection systems.

"},{"location":"meshcentral/#mongodb-free-server-monitoring","title":"MongoDB free server monitoring","text":"

If running with MongoDB version 4.x, there is a free database monitoring service that is provided. Just run \u201cmongo\u201d and you may see the following:

Type \u201cdb.enableFreemonitoring()\u201d if you want to enable this. You will be given a URL to access the data and can turn it back off at any time. The web page will look something like this:

In addition to database specific information, the graphs track CPU, memory and disk usage. This can be useful to track how well the server is responding under load.

"},{"location":"meshcentral/#meshcentral-single-sign-on-sso","title":"MeshCentral Single Sign-On (SSO)","text":"

As with any web application deployed in organization, it\u2019s convenient and more secure for users to have a single set of credentials that can be used across many services. In this section we take a look at how to configure MeshCentral so that you can sign-in using credentials from other services. This allows users to completely skip creating a user account on MeshCentral or having to remember usernames and password for one more web site. There are two single sign-on protocols that are supported in MeshCentral, OAuth2 and SAML. We will take a look at an example for each one.

Before you get started, your MeshCentral server must be publicly facing on the internet and have a valid TLS certificate. For example, by setting up Let\u2019s Encrypt. After the web site is working correctly user the steps below.

"},{"location":"meshcentral/#ldap","title":"LDAP","text":"

You can integrate LDAP using these configuration options

"},{"location":"meshcentral/#twitter-authentication","title":"Twitter Authentication","text":"

Like many other services, Twitter allows its users to login to other web site using Twitter credentials using OAuth2. Start by creating an account on Twitter and logging in. Then navigate to https://developer.twitter.com/en/apps, this is where you can create new applications that are compatible with Twitter.

Start by creating a new application and fill in the application form. Give your application and name, description, server URL and more.

Make sure to select \u201cEnable Sign in with Twitter\u201d and set the callback URL to \u201chttps://(server.domain.com)/auth-twitter-callback\u201d. This is the URL that Twitter will redirect users to once they are logged in. For example this is what a sample application would look like:

Once the new application is created, go to the \u201cKeys and tokens\u201d tab. You will need the \u201cAPI Key\u201d and \u201cAPI secret key\u201d values. In the MeshCentral config.json, place these two values as \u201cclientid\u201d and \u201cclientsecret\u201d of the Twitter section of the \u201cAuthStrategies\u201d.

Once done, your config.json should look a bit like this:

{\n  \"settings\": {\n    \"Cert\": \"myserver.mydomain.com\",\n    \"Port\": 443,\n    \"RedirPort\": 80\n  },\n  \"domains\": {\n    \"\": {\n      \"Title\": \"MyServer\",\n      \"Title2\": \"Servername\",\n      \"NewAccounts\": true,\n      \"authStrategies\": {\n        \"twitter\": {\n          \"clientid\": \"xxxxxxxxxxxxxxxxxxxxxxx\",\n          \"clientsecret\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n        }\n      }\n    }\n  }\n}\n

Note that if you do not allow new accounts, any new users that use Twitter credentials will not be able to login to MeshCentral. One trick is to allow new account, login and change this setting again. Once the config.json is correct, restart the server and you should see the Twitter icon on the login screen. When restarting the MeshCentral server, new modules will need to be installed to support this new feature. Depending on how your server is setup, you may need to restart the server manually to allow the new modules to be installed.

"},{"location":"meshcentral/#google-github-reddit-authentication","title":"Google, GitHub, Reddit Authentication","text":"

The exact same process as shown in the previous section can be repeated for Google, GitHub and Reddit. In each case, you need to go to each respective credential provider and get a \u201cClientID\u201d and \u201cClientSecret\u201d for each service. You also need to register the correct callback URL for each service. Take a look at the config.json below and note the callback URL that will need to be registered for each service provider.

{\n  \"settings\": {\n    \"Cert\": \"myserver.mydomain.com\",\n    \"Port\": 443,\n    \"RedirPort\": 80\n  },\n  \"domains\": {\n    \"\": {\n      \"Title\": \"MyServer\",\n      \"Title2\": \"Servername\",\n      \"NewAccounts\": true,\n      \"authStrategies\": {\n        \"twitter\": {\n          \"__callbackurl\": \"https://server/auth-twitter-callback\",\n          \"clientid\": \"xxxxxxxxxxxxxxxxxxxxxxx\",\n          \"clientsecret\": \" xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \"\n        },\n        \"google\": {\n          \"__callbackurl\": \"https://server/auth-google-callback\",\n          \"clientid\": \"xxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com\",\n          \"clientsecret\": \"xxxxxxxxxxxxxxxxxxxxxxx\"\n        },\n        \"github\": {\n          \"__callbackurl\": \"https://server/auth-github-callback\",\n          \"clientid\": \"xxxxxxxxxxxxxxxxxxxxxxx\",\n          \"clientsecret\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n        },\n        \"reddit\": {\n          \"__callbackurl\": \"https://server/auth-reddit-callback\",\n          \"clientid\": \"xxxxxxxxxxxxxxxxxxxxxxx\",\n          \"clientsecret\": \" xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \"\n        }\n      }\n    }\n  }\n}\n

It\u2019s possible to enable all four of these service providers at the same time to offer the most flexibility for users. Note that when using an OAuth service provider, MeshCentral does not offer two-factor authentication since it will be handled by the provider depending on user configuration.

"},{"location":"meshcentral/#microsoft-azure-active-directory","title":"Microsoft Azure Active Directory","text":"

In this section we look at how to setup MeshCentral to Azure Active Directory using OAuth. Like all other sections about setting up single sign-on, make sure your MeshCentral server is already setup on the public Internet with a valid TLS certificate. You can then start by adding a new application registration to the Azure portal.

We give our application a name, generally the domain name of the MeshCentral server is a good choice. Then you can setup the redirect URL to https://[servername]/auth-azure-callback. Make sure to type this correctly, all lower case with the full domain name of your MeshCentral server. Once done, there are two values we will need later, the Application ID and Tenant ID.

Next, we need to create a secret that will be shared between Azure and MeshCentral. Go to the \u201cCertificates & secrets\u201d section and click \u201cNew client secret\u201d. You then enter a name and for our example, we will opt to never make it expire.

We then copy the resulting secret and this will be the 3rd and final value we need to get MeshCentral setup. Now, we take the application ID, tenant ID and secret and place these values in the MeshCentral config.json like so:

{\n  \"settings\": {\n    \"Cert\": \"myserver.mydomain.com\",\n    \"Port\": 443,\n    \"RedirPort\": 80\n  },\n  \"domains\": {\n    \"\": {\n      \"Title\": \"MyServer\",\n      \"Title2\": \"Servername\",\n      \"NewAccounts\": false,\n      \"authStrategies\": {\n        \"azure\": {\n          \"newAccounts\": true,\n          \"clientid\": \"be4aadd3-77b8-4e55-af8a-4b8e2d994cb5\",\n          \"clientsecret\": \"NP0XXXXXXXXXXXXXXXXXXX\",\n          \"tenantid\": \"18910a48-e492-4c49-8043-3449f7964bd6\"\n        }\n      }\n    }\n  }\n}\n

The \u201cApplication ID\u201d value is placed as \u201cClient ID\u201d in the configuration file. You can also see that in the example above, we have \u201cNewAccounts\u201d set to false in the default MeshCentral domain, but set to true in the Azure section. This indicates that new accounts are not allowed in this domain except if it\u2019s a new user that is authenticating thru Azure. Once done, restart the MeshCentral server. Depending on your setup, you many need to run MeshCentral once manually to allow new required modules to be installed. Once running again, you should see the Azure single sign-on button on the login page.

"},{"location":"meshcentral/#jumpcloud-authentication-using-saml","title":"JumpCloud Authentication using SAML","text":"

While using OAuth may be interesting, it\u2019s more likely that MeshCentral servers used in an enterprise environment will want to use SAML (Security Assertion Markup Language). This is a widely deployed sign-on protocol used in enterprises so that, for example, employees can login to many different web sites using a single set of company credentials. MeshCentral can be one of many web sites that some users may want to log into.

In this section, we setup MeshCentral with JumpCloud, an easy to use sign-in provider. You can create an account on JumpCloud for free with up to 10 users allowing you to quickly get setup and test the following setup. In the next section, we look at a generic SAML configuration.

Before getting started with this section, make sure your server is on the Internet and publicly available and that it has a valid TLS certificate. You can use Let\u2019s Encrypt to get a valid TLS certificate. Then, start by going to https://jumpcloud.com and creating an administrator account. Once setup, go to \u201cApplications\u201d and click on the big plug sign to create a new application.

You will need to create a custom SAML application by clicking the \u201cCustom SAML App\u201d.

Then, you can fill in the form with an application name and logo.

  • For the IdP Entity ID, put \u201cjumpcloud\u201d.
  • For the SP Entity ID put \u201cmeshcentral\u201d.
  • For the ACS URL, put the callback URL of your server. In this case it will be \u201chttps://(yourservername)/auth-jumpcloud-callback\u201d
  • Lastly in the attributes section, add 3 user attribute mapping.
    • \u201cfirstname\u201d to \u201cfirstname\u201d
    • \u201clastname\u201d to \u201clastname\u201d
    • \u201cemail\u201d to \u201cemail\u201d

The attribute mappings will allow MeshCentral to receive from JumpCloud the first and last name of the user and the email address of the use. If any of these values are changed in the future, MeshCentral will update them the next time the user logs into MeshCentral. Here is an example configuration with red arrows next to important values.

Once setup, you will need to allow one or more users to use the new application. One way to do this is to just add your new application to the \u201cAll Users\u201d group.

We are now almost done with JumpCloud. The last thing we need to do is download the certificate that JumpCloud will be using to sign the SAML assertions. You can get this certificate by going in the \u201cApplications\u201d tab, click on your new application and select \u201cDownload Certificate\u201d as shown here.

Save the certificate as \u201cjumpcloud-saml.pem\u201d and place it in the \u201cmeshcentral-data\u201d folder. You are now ready to configure MeshCentral. Edit the config.json and make it look like this:

{\n  \"settings\": {\n    \"Cert\": \"myserver.mydomain.com\",\n    \"Port\": 443,\n    \"RedirPort\": 80\n  },\n  \"domains\": {\n    \"\": {\n      \"Title\": \"MyServer\",\n      \"Title2\": \"Servername\",\n      \"NewAccounts\": false,\n      \"authStrategies\": {\n        \"jumpcloud\": {\n          \"__callbackurl\": \"https://server/auth-jumpcloud-callback\",\n          \"NewAccounts\": true,\n          \"entityid\": \"meshcentral\",\n          \"idpurl\": \"https://sso.jumpcloud.com/saml2/saml2\",\n          \"cert\": \"jumpcloud-saml.pem\"\n        }\n      }\n    }\n  }\n}\n

Take note that the \u201centityid\u201d, \u201cidpurl\u201d and \u201ccert\u201d are values taken from JumpCloud. The callback URL should be configured in JumpCloud as we have done in previous steps. You can see that in the example above, we have \u201cNewAccounts\u201d set to false in the default MeshCentral domain, but set to true in the JumpCloud section. This indicates that new accounts are not allowed in this domain except if it\u2019s a new user that is authenticating thru JumpCloud.

You are now ready to restart the MeshCentral server. Extra modules will be needed to support SAML and so, depending on your server configuration, you may need to run MeshCentral manually once to allow the new modules to be installed from NPM. Once restarted, you should see the JumpCloud sign-in button on the login screen.

Users can sign-in using the regular username and password or using JumpCloud.

"},{"location":"meshcentral/#generic-saml-setup","title":"Generic SAML setup","text":"

In this section, we look at configuring SAML with a generic authentication provider. The setup is exactly the same as with JumpCloud in the previous section, but we will be using a different section in the config.json to that a generic login icon is shown on the login page.

A generic SAML setup will look like this:

{\n  \"settings\": {\n    \"Cert\": \"myserver.mydomain.com\",\n    \"Port\": 443,\n    \"RedirPort\": 80\n  },\n  \"domains\": {\n    \"\": {\n      \"Title\": \"MyServer\",\n      \"Title2\": \"Servername\",\n      \"NewAccounts\": 1,\n      \"authStrategies\": {\n        \"saml\": {\n          \"__callbackurl\": \"https://server/auth-saml-callback\",\n          \"entityid\": \"meshcentral\",\n          \"idpurl\": \"https://server/saml2\",\n          \"cert\": \"saml.pem\"\n        }\n      }\n    }\n  }\n}\n

The callback URL will be of the form \u201chttps://(servername)/auth-saml-callback\u201d. You should set the entityid, idpurl as given by the identity provider. Lastly, place the identity provider certificate file in the \u201cmeshcentral-data\u201d folder and indicate the name of the file in \u201ccert\u201d. Once setup, restart the server and you should see a Single Sign-on button on the login screen.

Enabling SAML will require MeshCentral to install extra modules from NPM, so depending on your server configuration, you may need to run MeshCentral once manually.

Note

MeshCentral only supports \"POST\". For example Authentik's default setting is to use \"Redirect\" as a \"Service Provider Binding\".

"},{"location":"meshcentral/#generic-openid-connect-setup","title":"Generic OpenID Connect Setup","text":"

Generally, if you are using an IdP that supports OpenID Connect (OIDC), you can use a very basic configuration to get started, and if needed, add more specific or advanced configurations later. Here is what your config file will look like with a basic, generic, configuration.

{\n    \"settings\": {\n        \"cert\": \"mesh.your.domain\",\n        \"port\": 443,\n        \"sqlite3\": true\n    },\n    \"domains\": {\n        \"\": {\n            \"title\": \"Mesh\",\n            \"title2\": \".Your.Domain\",\n            \"authStrategies\": {\n                \"oidc\": {\n                    \"issuer\": \"https://sso.your.domain\",\n                    \"clientid\": \"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\",\n                    \"clientsecret\": \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\",\n                    \"newAccounts\": true\n                }\n            }\n        }\n  }\n}\n

As you can see, this is roughly the same as all the other OAuth2 based authentication strategies. These are the basics you need to get started using OpenID Connect because it's still authenticating with OAuth2. If you plan to take advantage of some of the more advanced features provided by this strategy you should consider reading the additional strategy documentation.

NOTE: MeshCentral will use https://mesh.your.domain/auth-oidc-callback as the default redirect uri.

"},{"location":"meshcentral/#improvements-to-meshcentral","title":"Improvements to MeshCentral","text":"

In 2007, the first version of MeshCentral was built. We will refer to it as \u201cMeshCentral1\u201d. When MeshCentral1 was designed, HTML5 did not exist and web sockets where not implemented in any of the major browsers. Many design decisions were made at the time that are no longer optimal today. With the advent of the latest MeshCentral, MeshCentral1 is no longer supported and MeshCentral v2 has been significantly redesigned and mostly re-written based of previous version. Here is a list of improvements made in MeshCentral when compared with MeshCentral1:

  • Quick Installation \u2013 By having MeshCentral published on NPM (www.npmjs.com) it\u2019s now easy to download and install MeshCentral on both Linux and Windows*. On Linux* you can use NPM directly (\u201cnpm install meshcentral\u201d) and on Windows you can use the .MSI installer.
  • Cross-Platform Support \u2013 Contrary to MeshCentral1 that only runs on Windows*, MeshCentral can run on any environment that supports NodeJS. This includes Windows*, Linux* and OSX*. Because MeshCentral runs on Linux, it often lowers hosting costs and makes it possible to run MeshCentral in a Docker* container environment.
  • Runs with Little Compute Resources \u2013 Typical MeshCentral1 installation requires a large disk space foot print (approx* 30G of disk space) and is compute intensive even for small deployments. MeshCentral requires little resources to host (70MB) and able to deliver reasonable performance on a 900Mhz CPU with 1GB RAM.
  • Multi-Tenancy and Load Balancing Support \u2013 MeshCentral can handle hosting many server instances at once. Each instance or \u201cdomain\u201d has it\u2019s own administrators, users and computers to manage. The server can handle each instance using a url path \u201cserver.com/customer1\u201d or a DNS name \u201ccustomer1.server.com\u201d. Many customers can be handled by having all the DNS names point to the same server IP address. MeshCentral will take care of serving the right TLS certificate for each connection.
  • Single Executable \u2013 MeshCentral is a single-module or single executable server. All of the components of MeshCentral1 including IIS, Swarm, AJAX, Social, Manageability Servers are all build into one single executable. This makes it super easy to setup and run, it also minimizes problems and overhead caused by having many components communicate to each other. When the server is updated, all of the components are updated at once and effective.
  • Web Application Design \u2013 MeshCentral1 has 100\u2019s of web pages and often times a click on a web page causes the browser to load a different web page and this creates more load on the server. With MeshCentral there are only two main web pages: The login page and the main web application. This design is much more responsive since the server now delegates most of the UI workload to the client\u2019s web browser.
  • Real-Time User Interface \u2013 In MeshCentral, the user never has to hit the \u201crefresh\u201d button to update the web page. The web interface is completely real-time and updates as things change. MeshCentral uses websockets to connect to the server and get real-time events.
  • Single Programming Language \u2013 MeshCentral1 used JavaScript on the browser, C# on the server and C for the agent. Use of 3 different programming languages means that developers wanting to implement a new use-case needs to have sufficient skills to change between these 3 languages during the coding session. Makes the code significantly more difficult to understand and maintain.
  • Support for LAN only Mode \u2013 MeshCentral is capable of being setup as \u201cLAN only\u201d mode. In fact, this is the default mode when no static name or IP address is provided. In this mode, MeshAgents perform a multicast search on the network for the server making a static DNS/IP unnecessary.
  • Support for TLS Offloaders \u2013 TLS offloaders are now fully supported. This means that MeshCentral can handle way more network connections and traffic significantly.
  • Support for CIRA User/Pass Login \u2013 MeshCentral now supports both Intel AMT CIRA user/pass login and certificate login. Compared to MeshCentral1 that only supported certificate login, user/pass login is easier to setup and it can also be used for TLS offloaders and CIRA authentication.
  • No Live State Stored in the Database \u2013 One if the big problems with MeshCentral1 is that a lot of the live states (Agent, User and AMT connections and disconnections) needed to be stored in the database. This caused a few problems, first the extra load on the database that was un-necessary, but also that servers did not have real-time state information about other servers (they had to query the database). This resulted in more load on the database and scaling issues. In MeshCentral, all live states are kept in the RAM which boosts performance significantly.
  • Agentless Intel AMT Support \u2013 With MeshCentral1, administrators have to install the MeshAgent software on all computers, even if it was only for used for Intel AMT. MeshCentral supports a new agent-less mesh type that allows administrators to just setup the server strictly for Intel AMT only.
  • Latest Security & Crypto algorithms \u2013 MeshCentral uses all the latest cryptographic algorithm, notably SHA384 and RSA3072 making it more resistant to future quantum computer attacks. This would be very difficult to retrofit into MeshCentralv1 since it would require change of database schema and 1000\u2019s of line of code thus making the server incompatible with the current version version, making migration difficult.
  • Support for Email Verification and Password Recovery \u2013 MeshCentral can be configured with an SMTP server to send out e-mail confirmation messages and password recovery message. This is an important feature that was missing in MeshCentral1.
  • MeshInterceptor Support \u2013 MeshCentral can insert HTTP and Intel AMT redirection credential into a live data stream. This is useful to allow an administrator to securely pass Intel AMT password and control over an Intel AMT computer via web browser without the additional administrator login UI.

It\u2019s possible to perform migration to MeshCentral from MeshCentral1 server using a migration package. The MeshCentral Migration Tool will convert your existing user database into a format that can be imported into MeshCentral.

In addition to the migration tool, MeshCentral has a special module that will update all MeshAgents from v1 to v2 so the transition should be simple.

"},{"location":"meshcentral/#additional-resources","title":"Additional Resources","text":"

In addition to this document, there are a growing set of MeshCentral tutorial videos available on YouTube which covers all of the basic at www.meshcommander.com/meshcentral2/tutorials. The tutorial includes videos on how to perform server installation using both the Windows MSI installer and NPM methods.

"},{"location":"meshcentral/#conclusion","title":"Conclusion","text":"

MeshCentral is a free, open source and powerful remote management solution that is cross-platform. In this document, we have covered in detail on how to install and configure MeshCentral server to meet specific environment and use-case. MeshCentral works in many environments and situations. MeshCentral is not only simple to install but also takes minimal resources to host which makes it a very good remote management solution. As with any good software, MeshCentral will continue to be updated and evolve.

"},{"location":"meshcentral/#license","title":"License","text":"

MeshCentral and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0.

"},{"location":"meshcentral/#annex-1-sample-configuration-file","title":"Annex 1: Sample Configuration File","text":"

In this annex, we present a complete sample config.json file. You would put this file in the \u201cmeshcentral-data\u201d folder that is created when MeshCentral is first run. The config.json is completely optional and the server will run with default values with it. All key names in this file are case insensitive.

{\n  \"settings\": {\n    \"MongoDb\": \"mongodb://127.0.0.1:27017/meshcentral\",\n    \"MongoDbCol\": \"meshcentral\",\n    \"Port\": 4430,\n    \"AliasPort\": 443,\n    \"RedirPort\": 800,\n    \"TlsOffload\": \"127.0.0.1\",\n    \"MpsPort\": 44330,\n    \"MpsAliasPort\": 4433,\n    \"MpsTlsOffload\": true,\n    \"SessionTime\": 30,\n    \"SessionKey\": \"MyReallySecretPassword\",\n    \"AllowLoginToken\": true,\n    \"AllowFraming\": true,\n    \"WebRTC\": true,\n    \"ClickOnce\": true\n  },\n  \"domains\": {\n    \"\": {\n      \"Title\": \"MyServer\",\n      \"Title2\": \"Servername\",\n      \"TitlePicture\": \"title-sample.png\",\n      \"UserQuota\": 1048576,\n      \"MeshQuota\": 248576,\n      \"NewAccounts\": true,\n      \"Footer\": \"<a href='https://twitter.com/mytwitter'>Twitter</a>\",\n      \"PasswordRequirements\": { \"min\": 8, \"max\": 128, \"upper\": 1, \"lower\": 1, \"numeric\": 1, \"nonalpha\": 1 }\n    },\n    \"customer1\": {\n      \"Dns\": \"customer1.myserver.com\",\n      \"Title\": \"Customer1\",\n      \"Title2\": \"TestServer\",\n      \"NewAccounts\": 1,\n      \"Auth\": \"sspi\",\n      \"Footer\": \"Test\"\n    },\n    \"info\": {\n      \"share\": \"C:\\\\ExtraWebSite\"\n    }\n  },\n  \"letsencrypt\": {\n    \"email\": \"myemail@myserver.com \",\n    \"names\": \"myserver.com,customer1.myserver.com\",\n    \"rsaKeySize\": 3072,\n    \"production\": false\n  },\n  \"peers\": {\n    \"serverId\": \"server1\",\n    \"servers\": {\n      \"server1\": { \"url\": \"wss://192.168.2.133:443/\" },\n      \"server2\": { \"url\": \"wss://192.168.1.106:443/\" }\n    }\n  },\n  \"smtp\": {\n    \"host\": \"smtp.myserver.com\",\n    \"port\": 25,\n    \"from\": \"myemail@myserver.com\",\n    \"tls\": false\n  }\n}\n

All these values are examples only, this config.json should just be used as an example and none of the values here are real.

"},{"location":"meshcentral/#annex-2-tips-tricks","title":"Annex 2: Tips & Tricks","text":"

In this annex, we present various suggestions. These are often found by users on the GitHub community and readers are encouraged to participate. The GitHub community is at: https://github.com/Ylianst/MeshCentral/issues

"},{"location":"meshcentral/#remote-terminal","title":"Remote Terminal","text":"

When doing a remote terminal session to a Linux computer, it may be interesting to run the bash shell under a different user. One would typically use the command:

su -s /bin/bash myOtherUser\n

However, because bash is not run in interactive mode, the command line prompt may be empty and history keys (up and down), tab and backspace will not work right. The correct command is:

su -c '/bin/bash -i' myOtherUser\n

This will run bash in interactive mode and work correctly.

"},{"location":"meshcentral/#ssh-and-sftp-integration-to-the-terminal","title":"SSH and SFTP integration to the Terminal","text":"

MeshCentral has built-in web-based integration of SSH in the \"Terminal\" tab and SFTP in the \"Files\" tab.

"},{"location":"meshcentral/SSLnletsencrypt/","title":"SSL/Letsencrypt","text":""},{"location":"meshcentral/SSLnletsencrypt/#meshcentral-supports-ssl-using-self-generated-certs-your-own-certs-or-letsencrypt","title":"MeshCentral supports SSL using self generated certs, your own certs or Letsencrypt","text":""},{"location":"meshcentral/SSLnletsencrypt/#enabling-letsencrypt","title":"Enabling letsencrypt","text":"

Make sure you match and/or adjust all the following settings appropriately in your config.json file:

{\n    \"settings\": {\n        \"redirPort\"\n        \"cert\": \"yourdomain.com\"\n    },\n    \"domains\": {\n        \"letsencrypt\": {\n        \"__comment__\": \"Requires NodeJS 8.x or better, Go to https://letsdebug.net/ first before trying Let's Encrypt.\",\n        \"email\": \"myemail@myserver.com\",\n        \"names\": \"myserver.com,customer1.myserver.com\",\n        \"skipChallengeVerification\": false,\n        \"production\": true\n        },\n    }\n}\n

If you need further clarification to know what each of these settings are, check out the config schema.

Then restart meshcentral and it will get a cert for you, the process will need to restart to apply the cert.

"},{"location":"meshcentral/SSLnletsencrypt/#useful-resourcestroubleshooting","title":"Useful resources/troubleshooting","text":"

To check letsencrypt is working properly please use https://letsdebug.net/. We are using the HTTP-O1 challenge method with these instructions.

Also make sure you have port 80 open and pointing to your meshcentral server, IT WILL NOT WORK if port 80 isn't open and it HAS to be port 80.

You can read more about Letsencrypt and meshcentral here.

"},{"location":"meshcentral/agents/","title":"Mesh Agents","text":""},{"location":"meshcentral/agents/#windows","title":"Windows","text":"

Default Install Path: c:\\Program Files\\Mesh Agent

Application Path: c:\\Program Files\\Mesh Agent\\meshagent.exe

Application database Path: c:\\Program Files\\Mesh Agent\\meshagent.db

Application Log Path: c:\\Program Files\\Mesh Agent\\meshagent.log

xxx Path: c:\\Program Files\\Mesh Agent\\meshagent.msh

Status Troubleshooting
  • Start: net start \"mesh agent\"
  • Stop: net stop \"mesh agent\"
  • Restart: net restart \"mesh agent\"
  • Status: Needs info

Troubleshooting steps: Needs info

"},{"location":"meshcentral/agents/#linux-bsd","title":"Linux / BSD","text":"

Uninstall: sudo /usr/local/mesh_services/meshagent/[agent-name]/meshagent -fulluninstall

"},{"location":"meshcentral/agents/#apple-macos-binary-installer","title":"Apple macOS Binary Installer","text":"

Default Install Path: /usr/local/mesh_services/meshagent/meshagent

Launches from /Library/LaunchAgents/meshagent.plist

Controlling agent

launchctl stop meshagent\nlaunchctl start meshagent\n

Install:

Uninstall: sudo /usr/local/mesh_services/meshagent/[agent-name]/meshagent -fulluninstall

"},{"location":"meshcentral/agents/#apple-macos-universal","title":"Apple macOS Universal","text":"

For OSx 11+ including Big Sur, Monterey and later

"},{"location":"meshcentral/agents/#apple-macos","title":"Apple macOS","text":"

For macOS 10.x including Catalina, Mojave, High Sierra, Sierra, El Capitan, Yosemite, Mavericks, Mountain Lion and earlier.

"},{"location":"meshcentral/agents/#mobile-device-android","title":"Mobile Device (Android)","text":""},{"location":"meshcentral/agents/#meshcentral-assistant","title":"MeshCentral Assistant","text":"

See Assistant

"},{"location":"meshcentral/agents/#apple-macos-binary-installer_1","title":"Apple MacOS Binary Installer","text":""},{"location":"meshcentral/agents/#agent-commands","title":"Agent Commands","text":"

agentmsg : Add/Remove badged messages to the device's web ui

  agentmsg add \"[message]\" [iconIndex]\n  agentmsg remove [index]\n  agentmsg list\n
agentsize : Returns the binary size of the agent

agentupdate : Manually trigger an agent self-update

alert : Display an alert dialog on the logged in session

alert TITLE, CAPTION [, TIMEOUT]\n

amt

amtconfig

amtevents

apf

args

av : Displays Antivirus State

coredump

coreinfo

cpuinfo

cs : Display Windows Connected Standby State

dbcompact : Compacts the agent database

dbget

dbkeys

dbset

dnsinfo : Display DNS server info

domain : Display domain metadata

errorlog

eval : executes javascript on the agent

eval [code]\n

fdcount : Returns the number of active descriptors in the event loop

fdsnapshot : Returns detailed descriptor/handle/timer metadata

getclip : Fetches clipboard data from agent

getscript

help : Returns the list of supported console commands

httpget

info : Returns general information about the agent, such as connected state, loaded modules, LMS state, etc

kill : Sends a SIGKILL signal to the specified PID

kill [pid]\n

kvmmode : Displays the KVM Message Format

location : Displays saves location information about the connected agent

lock

log : Writes a message to the logfile

log [message]\n

ls : Enumerates the files in the agent's install folder

mousetrails : Enables/Disables Mouse Trails Accessibility on Windows. To change setting, specify a positive integer representing the number of latent cursors, where 0 is disable

mousetrails [n]\n

msh : Displays the loaded msh settings file

netinfo : Displays network interface information

notify : Display a notification on the web interface

openurl

osinfo : Displays OS information

parseuri : Parses the specified URI, and displays the parsed output

parseuri [uri]\n

plugin : Invokes a plugin

plugin [pluginName] [args]\n

power : Performs the specified power action

power [action]\n  LOGOFF = 1\n  SHUTDOWN = 2\n  REBOOT = 3\n  SLEEP = 4\n  HIBERNATE = 5\n  DISPLAYON = 6\n  KEEPAWAKE = 7\n  BEEP = 8\n  CTRLALTDEL = 9\n  VIBRATE = 13\n  FLASH = 14\n

print

privacybar : Sets/Gets the default pinned state of the Privacy Bar on windows

privacybar [PINNED|UNPINNED]\n

ps : Enumerates processes on the agent

rawsmbios : Fetches the raw smbios table

safemode : Sets/Gets the SAFEMODE configuration of the agent, as well as the next boot state.

safemode (ON|OFF|STATUS)\n

scanwifi : Scans the available Wifi access points, and displays the SSID and Signal Strength

service : Shortcut to be able to restart the agent service

service status|restart\n

setclip : Sets clipboard data to the agent

setclip [text]\n

setdebug : Sets the location target for debug messages

setdebug [target]\n0 = Disabled\n1 = StdOut\n2 = This Console\n* = All Consoles\n4 = WebLog\n8 = Logfile\n

smbios : Displays the parsed SMBIOS metadata

startupoptions : Displays the command-line options that the agent was started with

sysinfo : Collects and displays telemetry on the platform

task

taskbar : Hides or shows the Windows System task bar, optionally on the specified Terminal Server Session ID

taskbar HIDE|SHOW [TSID]\n

timerinfo : Displays metadata about any configured timers on the event loop

toast : Displays a toast message on the logged in user's session

toast [message]\n

translations : Shows the currently configured translations

type

type (filepath) [maxlength]\n

uac : Get/Sets the Windows UAC mode

uac [get|interactive|secure]\n

unzip

unzip input, destination\n
: Unzips the specified file

users : Enumerates the logged in users on the system

versions : Displays version information about the agent

vm : Detects if the system is a Virtual Machine

volumes : Displays volume information reported by the OS

wakeonlan : Sends wake-on-lan packets to the specified MAC address

wakeonlan [mac]\n

wallpaper : Gets/Toggles the logged in user's desktop background image

wallpaper (GET|TOGGLE)\n

wpfhwacceleration : Enable/Disable WPF HW Acceleration on Windows

wpfhwacceleration (ON|OFF|STATUS)\n

wsclose

wsconnect

wslist

wssend

zip

zip (output file name), input1 [, input n]\n

"},{"location":"meshcentral/agents/#agent-msh-options","title":"Agent msh options","text":"

You can find a full list of options for the agent here

skipmaccheck=1: Will not regenerate the agents nodeid and cause duplication of the agent when the MAC address changes.

You can add options to your .msh on agent install with this

"},{"location":"meshcentral/assistant/","title":"MeshCentral Assistant","text":""},{"location":"meshcentral/assistant/#initial-setup","title":"Initial Setup","text":""},{"location":"meshcentral/assistant/#agent-invite-code","title":"Agent Invite Code","text":"
\"domains\": {\n    \"\": {\n        \"agentInviteCodes\": true\n    }\n}\n
"},{"location":"meshcentral/assistant/#agent-invitation","title":"Agent Invitation","text":"

Click on the 'Invite' button next to the device group name to access it.

"},{"location":"meshcentral/assistant/#link-invitation","title":"Link Invitation","text":"

For link invitation web page customization:

  1. Alongside meshcentral-data create a folder called meshcentral-web
  2. Create a views folder in it and copy the file node_modules/meshcentral/views/invite.handlebars into it.
  3. That copy will be served instead of the default one, so you can customize it as you want.

"},{"location":"meshcentral/assistant/#email-invitation","title":"Email Invitation","text":"

This option will show up if you have an SMTP email server set up with MeshCentral.

For invitation email customization:

  1. Alongside meshcentral-data create a folder called meshcentral-web
  2. Create an emails folder in it and copy the files node_modules/meshcentral/emails/mesh-invite.txt and node_modules/meshcentral/emails/mesh-invite.html into it.
  3. These copies will be used instead of the default ones, so you can customize them as you want.

"},{"location":"meshcentral/assistant/#email-notification","title":"Email notification","text":"

You can also get an email notification when someone clicks the \"Request Help\" button in the Assistant agent.

"},{"location":"meshcentral/codesigning/","title":"Code Signing","text":""},{"location":"meshcentral/codesigning/#authenticode-js-video","title":"Authenticode-JS Video","text":"

Nodejs Code Signing module

MeshCentral comes with authenticode.js, you can run it like this:

node node_modules/meshcentral/authenticode-js\n

and you will get

MeshCentral Authenticode Tool.\nUsage:\n  node authenticode.js [command] [options]\nCommands:\n  info: Show information about an executable.\n        --exe [file]             Required executable to view information.\n        --json                   Show information in JSON format.\n  sign: Sign an executable.\n        --exe [file]             Required executable to sign.\n        --out [file]             Resulting signed executable.\n        --pem [pemfile]          Certificate & private key to sign the executable with.\n        --desc [description]     Description string to embbed into signature.\n        --url [url]              URL to embbed into signature.\n        --hash [method]          Default is SHA384, possible value: MD5, SHA224, SHA256, SHA384 or SHA512.\n        --time [url]             The time signing server URL.\n        --proxy [url]            The HTTP proxy to use to contact the time signing server, must start with http://\n  unsign: Remove the signature from the executable.\n        --exe [file]             Required executable to un-sign.\n        --out [file]             Resulting executable with signature removed.\n  createcert: Create a code signging self-signed certificate and key.\n        --out [pemfile]          Required certificate file to create.\n        --cn [value]             Required certificate common name.\n        --country [value]        Certificate country name.\n        --state [value]          Certificate state name.\n        --locality [value]       Certificate locality name.\n        --org [value]            Certificate organization name.\n        --ou [value]             Certificate organization unit name.\n        --serial [value]         Certificate serial number.\n  timestamp: Add a signed timestamp to an already signed executable.\n        --exe [file]             Required executable to sign.\n        --out [file]             Resulting signed executable.\n        --time [url]             The time signing server URL.\n        --proxy [url]            The HTTP proxy to use to contact the time signing server, must start with http://\n  icons: Show the icon resources in the executable.\n        --exe [file]               Input executable.\n  saveicons: Save an icon group to a .ico file.\n        --exe [file]               Input executable.\n        --out [file]               Resulting .ico file.\n        --icongroup [groupNumber]  Icon groupnumber to save to file.\n        --removeicongroup [number]\n        --icon [groupNumber],[filename.ico]\n\nNote that certificate PEM files must first have the signing certificate,\nfollowed by all certificates that form the trust chain.\n\nWhen doing sign/unsign, you can also change resource properties of the generated file.\n\n          --filedescription [value]\n          --fileversion [value]\n          --internalname [value]\n          --legalcopyright [value]\n          --originalfilename [value]\n          --productname [value]\n          --productversion [value]\n
"},{"location":"meshcentral/codesigning/#automatic-agent-code-signing","title":"Automatic Agent Code Signing","text":"

If you want to self-sign the mesh agent so you can whitelist the software in your AV, as well as lock it to your server and organization:

Note

If you generate your private key on windows with use BEGIN PRIVATE KEY and openssl needs BEGIN RSA PRIVATE KEY you can convert your private key to rsa private key using openssl rsa -in server.key -out server_new.key

"},{"location":"meshcentral/codesigning/#setting-agent-file-info","title":"Setting Agent File info","text":"

Now that MeshCentral customizes and signs the agent, you can set that value to anything you like.

\"domains\": {\n      \"agentFileInfo\": {\n            \"filedescription\": \"sample_filedescription\",\n            \"fileversion\": \"0.1.2.3\",\n            \"internalname\": \"sample_internalname\",\n            \"legalcopyright\": \"sample_legalcopyright\",\n            \"originalfilename\": \"sample_originalfilename\",\n            \"productname\": \"sample_productname\",\n            \"productversion\": \"v0.1.2.3\"\n      }\n}\n
"},{"location":"meshcentral/config/","title":"Configuration Options","text":"

There are MANY configuration options available with meshcentral, search this file for options: https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json

Some options you can find relate to:

  • DNS
  • HTTPS
  • MPS (Management Presence Server)
  • MongoDB
  • MariaDB
  • SQLite3
  • MySQL
  • PostgreSQL
  • AceBase
  • WAN (Wide Area Network)
  • LAN (Local Area Network)
  • Maintenance Mode
  • Session Cookie
  • Database Encryption
  • Web Relay
  • Agent Connection
  • TLS (Transport Layer Security)
  • WebRTC
  • Web Push Notifications
  • Auto Backup
  • Crowdsec
  • IP KVM (Keyboard, Video, Mouse over IP)
  • Mesh Router
  • Syslog
  • WebDAV
  • Certificates and Authentication
  • MeshCentral Server Settings
  • Device Management
  • User Permissions
  • Remote Desktop Configuration

and more!

"},{"location":"meshcentral/customization/","title":"Customization","text":"

Whitelabeling your MeshCentral installation to personalize it to your company's brand, as well as having your own terms of use is one of the first things many people do after installation.

"},{"location":"meshcentral/customization/#web-branding","title":"Web Branding","text":"

You can put your own logo on the top of the web page. To get started, get the file \u201clogoback.png\u201d from the folder \u201cnode_modules/meshcentral/public/images\u201d and copy it to your \u201cmeshcentral-data\u201d folder. In this example, we will change the name of the file \u201clogoback.png\u201d to \u201ctitle-mycompany.png\u201d. Then use any image editor to change the image and place your logo.

Once done, edit the config.json file and set one or all of the following values:

\"domains\": {\n  \"\": {\n    \"Title\": \"\",\n    \"Title2\": \"\",\n    \"TitlePicture\": \"title-sample.png\",\n    \"loginPicture\": \"logintitle-sample.png\",\n    \"welcomeText\": \"This is sample text\",\n    \"welcomePicture\": \"mainwelcome-04.jpg\",\n    \"welcomePictureFullScreen\": true,\n    \"siteStyle\": \"1\",\n    \"nightMode\": \"1\",\n    \"meshMessengerTitle\": \"Mesh Chat\",\n    \"meshMessengerPicture\": \"chatimage.png\",\n    \"footer\": \"This is a HTML string displayed at the bottom of the web page when a user is logged in.\",\n    \"loginfooter\": \"This is a HTML string displayed at the bottom of the web page when a user is not logged in.\"\n  },\n

This will set the title and sub-title text to empty and set the background image to the new title picture file. You can now restart the server and take a look at the web page. Both the desktop and mobile sites will change.

The title image must a PNG image of size 450 x 66.

You can also customize the server icon in the \u201cMy Server\u201d tab. By default, it\u2019s a picture of a desktop with a padlock.

If, for example, MeshCentral is running on a Raspberry Pi. You may want to put a different picture at this location. Just put a \u201cserver.jpg\u201d file that is 200 x 200 pixels in the \u201cmeshcentral-data\u201d folder. The time MeshCentral page is loaded, you will see the new image.

This is great to personalize the look of the server within the web site.

"},{"location":"meshcentral/customization/#customizing-web-icons","title":"Customizing Web Icons","text":"

MeshCentral lets you change the icons for different devices shown in the Web User Interface. To do this the proper way, you should make a new folder called meshcentral-web in the main directory, where you find other folders like meshcentral-data, meshcentral-backup, meshcentral-files, and node-modules. Inside meshcentral-web, make another folder named public and copy the entire node_modules/meshcentral/public/images folder into this new meshcentral-web/public folder and then edit the files in meshcentral-web/public/images/. This step is suggested because if MeshCentral updates, it might delete any changes in node_modules. But, changes in meshcentral-web will stay safe, and MeshCentral will use these files instead of the originals in node_modules.

To update device icons, you need to edit these files: meshcentral-web/public/images/webp/iconsXX.webp (icons16.webp, icons32.webp, icons50.webp, icons100.webp), and meshcentral-web/public/images/iconsXX.png (icons16.png, icons32.png, icons50.png, icons64.png, icons100.png) and the corresponding meshcentral-web/public/images/icons256-X-1.png. Make sure to keep the resolution of these files as it is.

By following these steps, you can customize any icon in MeshCentral. Just find and change the corresponding image files in the meshcentral-web/public/images folder. Similarly, you can also move other folders from node_modules/meshcentral to meshcentral-web while keeping the original folder structure. This allows you to modify other parts of MeshCentral too, like the .handlebars templates for the web interface. Simply copy files from node_modules/meshcentral/views to meshcentral-web/views and make your changes in meshcentral-web. This lets you match MeshCentral's look to your company's brand or your own style.

"},{"location":"meshcentral/customization/#customizing-agent-invitation","title":"Customizing Agent Invitation","text":"

Agents can be invited by public link or via email. Click Here to see details.

"},{"location":"meshcentral/customization/#agent-branding","title":"Agent Branding","text":"

You can customize the Agent to add your own logo, change the title bar, install text, the service name, or even colors!

Note

The Customization must be done FIRST and BEFORE you deploy your agents! Once the agents have been deployed, any customization made afterwards, will not sync! This is because the setup files are customized on the fly, then when you install the agents, the exe and .msh file with the customizations in are copied over to the required folder, so you will need to reinstall the agent for agent customizations to take effect.

\"domains\": {\n    \"\": {\n        \"agentCustomization\": {\n            \"displayName\": \"MeshCentral Agent\",\n            \"description\": \"Mesh Agent background service\",\n            \"companyName\": \"Mesh Agent Company\",\n            \"serviceName\": \"Mesh Agent Service\",\n            \"installText\": \"Text string to show in the agent installation dialog box\",\n            \"image\": \"mylogo.png\",\n            \"fileName\": \"meshagent\",\n            \"foregroundColor\": \"#FFA500\",\n            \"backgroundColor\": \"#EE82EE\"\n        }\n    }\n}\n

"},{"location":"meshcentral/customization/#terms-of-use","title":"Terms of use","text":"

You can change the terms of use of the web site by adding a \u201cterms.txt\u201d file in the \u201cmeshcentral-data\u201d folder. The file can include HTML markup. Once set, the server does not need to be restarted, the updated terms.txt file will get used the next time it\u2019s requested.

For example, placing this in \u201cterms.txt\u201d

<br />\nThis is a <b>test file</b>.\n

Will show this on the terms of use web page.

"},{"location":"meshcentral/debugging/","title":"Debugging","text":""},{"location":"meshcentral/debugging/#websockets-video","title":"Websockets Video","text":"

Make sure you understand how MeshCentral works with your browser using chrome developer tools.

"},{"location":"meshcentral/debugging/#meshcentral-server","title":"MeshCentral Server","text":""},{"location":"meshcentral/debugging/#useful-configjs-settings","title":"Useful config.js settings","text":"

https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json

\"AgentsInRAM\": false,\n\"AgentUpdateBlockSize\": 2048,\n\"agentUpdateSystem\": 1,\n\"noAgentUpdate\": 1,\n\"WsCompression\": false,\n\"AgentWsCompression\": false,\n
"},{"location":"meshcentral/debugging/#understanding-node-and-paths","title":"Understanding node and paths","text":"

Note that when running MeshCentral, you should always run from the path that is parent to node_modules, so you do this:

cd C:\\Program Files\\Open Source\\MeshCentral\nnode node_modules\\meshcentral\n

You do NOT do this:

cd C:\\Program Files\\Open Source\\MeshCentral\\node_modules\\meshcentral\nnode meshcentral\n

The problem with the second command is that NPM may install missing modules in the incorrect location.

Also, in general I recommend not using the MeshCentral MSI Installer and just install manually unless you are very scared of the command prompt. Anyone that knows a bit about the shell should install MeshCentral like this:

mkdir c:\\meshcentral\ncd c:\\meshcentral\nnpm install meshcentral\nnode node_modules\\meshcentral\n(ctrl-c when done installing optional modules)\nnode node_modules\\meshcentral --install\n

This way, you have a lot more control over what is going on. In my opinion, the MSI installer basically does the same thing and installs NodeJS for you.

"},{"location":"meshcentral/debugging/#unable-to-update-server","title":"Unable to update server","text":"

Generally the problem is that MeshCentral can't find the npm tool and therefore, can't run it to see if there is a new version. You can fix this by setting the path to npm in the config.json like this:

{\n  \"settings\": {\n    \"npmPath\": \"c:\\\\npm.exe\",\n    \"npmProxy\": \"http://1.2.3.4:80\"\n  }\n}\n

The problem could also be that you need a proxy, the configuration line to that is above.

You can also manually update. Just stop your server and so this:

mv node_modules node_modules_bak\nnpm install meshcentral\nnode node_modules/meshcentral\n

Then wait for all optional modules to install, then once the server starts hit ctrl-c and start up the server again. You can also use the following to help you start/stop the server:

node node_modules/meshcentral --install\nnode node_modules/meshcentral --uninstall\nnode node_modules/meshcentral --start\nnode node_modules/meshcentral --stop\n
"},{"location":"meshcentral/debugging/#port-troubleshooting-on-server","title":"Port Troubleshooting on server","text":"

If you're getting a port 4433 is not available error, this is because another process is using this port, very likely another instance of MeshCentral. If your MeshCentral server is bound to ports 81/444 MeshCentral could not get port 80/443 and got the next available ones.

In general the problem is that you are running two MeshCentral instances at the same time. Probably one as a background Windows Service and one in the command line. Which ever instance can grab port 4433 will have a running MPS and CIRA should work, but the second instance will not have port 4433 and CIRA will not work.

"},{"location":"meshcentral/debugging/#running-meshcentral-server-in-debug-mode","title":"Running Meshcentral server in debug mode","text":"

Debug more will cause MeshCentral to output a lot of debug messages to the console. To display all debug messages, run MeshCentral like this:

node node_modules/meshcentral --debug\n

A more practical way to run the debug command it to specify what messages you want printed out using a comma seperated list, for example:

node node_modules/meshcentral --debug web,amt,mps\n

Here is the list of all debug options:

cookie       - Cookie encoder\ndispatch     - Message Dispatcher\nmain         - Main Server Messages\npeer         - MeshCentral Server Peering\nagent        - MeshAgent traffic\nagentupdate  - MeshAgent update\ncert         - Server Certificate\ndb           - Server Database\nemail        - Email/SMS/Push Traffic\nweb          - Web Server\nwebrequest   - Web Server Requests\nrelay        - Web Socket Relay\nhttpheaders  - Web Server HTTP Headers\nauthlog      - User Authentication Log\namt          - Intel AMT\nwebrelay     - Connection Relay\nmps          - CIRA Server\nmpscmd       - CIRA Server Commands\n

You can also specify the debug option in the config.json file in the settings section. For example:

\"settings\": {\n  \"debug\": \"web,amt,mps\"\n}\n
"},{"location":"meshcentral/debugging/#enabling-trace-in-your-browser-dev-tools","title":"Enabling trace in your browser Dev Tools","text":"

You can enable browser console tracing by adding trace=1 as a parameter to the URL of the MeshCentral main web page. For example https://myserver.com/?trace=1. Once present, open the browser's console window to see all web client tracing messages.

To log all database queries, change log_statement in /etc/postgresql/13/main/postgresql.conf

# CUSTOM\nlog_statement = 'all'           # none, ddl, mod, all\n

The stacktrace was logged to stdout/journalctl. Supposedly, you can enable debug logging for node modules by adding DEBUG=<modulename> to the environment.

Adding this to /etc/systemd/system/meshcentral.service should do it but it didn't seem to do anything.

I think that's because Mesh uses the trace logging in the browser instead of logging things in the server logs.

Environment=DEBUG=mesh*\n

If you want to change node to meshcentral in journalctl, add this to /etc/systemd/system/meshcentral.service.

SyslogIdentifier=meshcentral\n
"},{"location":"meshcentral/debugging/#finding-agent-problems","title":"Finding agent problems","text":"

Using the servers My Server > console

  • agentstatus - Gives you summary counts of problems
  • agentissues - Gives IP:port and what error
  • dupagents - Gives duplicateAgent IP's, counts and names
"},{"location":"meshcentral/debugging/#logging-it-all","title":"Logging it all","text":"

To log everything that's possible, prepare the log directory.

mkdir /meshcentral/meshcentral-logs/\nchown tactical:tactical logs\nln -s ../meshcentral-logs/log.txt /meshcentral/meshcentral-data/log.txt\n

And then add this to your config.

    \"meshErrorLogPath\": \"/meshcentral/meshcentral-logs/\",\n    \"authLog\": \"/meshcentral/meshcentral-logs/auth.log\",\n    \"log\": [\n        \"cookie\",\n        \"dispatch\",\n        \"main\",\n        \"peer\",\n        \"web\",\n        \"webrequest\",\n        \"relay\",\n        \"webrelaydata\",\n        \"webrelay\",\n        \"mps\",\n        \"mpscmd\",\n        \"swarm\",\n        \"swarmcmd\",\n        \"agentupdate\",\n        \"agent\",\n        \"cert\",\n        \"db\",\n        \"email\",\n        \"amt\",\n        \"httpheaders\",\n        \"websocket\"\n    ],\n

You'll then have 3 files:

-rw-rw-r-- 1 tactical tactical   2593 Feb  2 12:22 auth.log\n-rw-r--r-- 1 tactical tactical 147593 Feb  2 12:31 log.txt\n-rw-rw-r-- 1 tactical tactical    381 Feb  2 12:02 mesherrors.txt\n

log.txt will now log everything in the Trace tab

"},{"location":"meshcentral/debugging/#restricting-server-to-specific-ips","title":"Restricting server to specific IP(s)","text":"

When doing debugging on my development server, I use this line in the settings section to block all agent connections except the agent I want:

\"agentAllowedIp\": [ \"192.168.2.147\" ],\n

Of course, this is just for debugging.

"},{"location":"meshcentral/debugging/#finding-system-id-types","title":"Finding system ID types","text":"

https://serverurl/meshagents aka trying figure out what this is

"},{"location":"meshcentral/debugging/#pull-down-cert-crt-file-from-internet","title":"Pull down cert .crt file from internet","text":"

See #1662 We have run into this challenge before, where our .crt file expired and then all our agents were unable to connect. In our case, the TLS cert was available on the internet, and thus, we were able to use these commands to update it:

echo -n \\| openssl s_client -connect yourdomain.com:443 2> /dev/null\\| sed -ne '/-BEGIN   CERTIFICATE-/,/-END CERTIFICATE-/p' > /opt/meshcentral/meshcentral-data/webserver-cert-public.crt\nservice meshcentral restart\n
"},{"location":"meshcentral/debugging/#meshagent","title":"MeshAgent","text":"

Troubleshooting agent

./meshagent -state\n
"},{"location":"meshcentral/debugging/#agent-debug-logs-to-server","title":"Agent Debug Logs to server","text":"

This automatically downloads all agent error logs into meshcentral-data/agenterrorlogs.txt

Set in config.json

\"agentLogDump\": true\n
"},{"location":"meshcentral/debugging/#determine-agent-capabilities","title":"Determine Agent capabilities","text":"

On the server goto the agents console tab. Type:

info\n
"},{"location":"meshcentral/debugging/#useful-meshagentmsh-flags","title":"Useful MeshAgent.msh flags","text":"

https://github.com/Ylianst/MeshAgent/blob/master/meshcore/agentcore.h#L190

controlChannelDebug=1\nlogUpdate=1\n
"},{"location":"meshcentral/debugging/#obtain-generated-msh-file","title":"Obtain generated .msh File","text":"

If you need a trick to get the .msh file, you can add ?debug=1 to the URL and click \"Add Agent\", there will be an extra link to download it.

"},{"location":"meshcentral/debugging/#meshagent-commands","title":"MeshAgent Commands","text":"
MeshAgent run\nMeshAgent dbTool.js list\n

Forcing Core version from Cmdline

  • Download meschore.js and rename to CoreModule.js and put it alongside MeshAgent.exe
  • Stop MeshAgent service
  • Run MeshAgent.exe dbTool.js import CoreModule
"},{"location":"meshcentral/debugging/#on-the-fly-patching-meshagent","title":"On the fly Patching MeshAgent","text":"

MeshAgent#89 (comment)

There are two ways to do this... When debugging, and making changes, you can modify the .js file directly, and just save it in the same folder as the agent binary... The agent will use the .js file from disc if it's there, if it's newer than the one compiled in the binary. You don't even need to restart the agent. You can just clear the core, and reload the core.....

When you are satisfied with your changes to the .js file, you can use the clipboard, in the following fashion:

meshagent -exec \"require('clipboard').nativeAddCompressedModule('foo');process.exit();\"\n

if the file you modified isn't in the same folder as the agent binary, you can use the following command if you don't want to move the file, and edit it directly in the modules folder:

meshagent -exec \"setModulePath('pathToFolder');require('clipboard').nativeAddCompressedModule('foo');process.exit();\"\n

This command is just like the previous, except it searches for modules in the path specified.

Just substitute foo, with the name of the module that you modified. It will load the module from disc, compress it, and save it into the clipboard.. So you can just load up your editor for ILibDuktape_Polyfills.c, and find where that particular module is defined... and paste directly from the clipboard... The clipboard will contain all the necessary C code to uncompress and load the module.

If the compressed result is relatively long, it will auto break it up into multiple lines to work around an issue with visual studio's maximum string literal limitations.

"},{"location":"meshcentral/debugging/#agent-debugging-using-meshcore-js-debugger","title":"Agent Debugging using MeshCore JS Debugger","text":"

(#119) How to test changes to the meshagent and recompile them.

  • Copy duktape-debugger.js to the mesh directory on the target machine.
  • From the console tab of the agent, enter this command, substituting the port number you want to use instead of 9999 eval \"attachDebugger({ webport: 9999 })\"

Then open your browser to http://localhost:9999 or whatever port you used.

Note

If you pause the debugger, and happen to forget about it, the agent will automatically kill itself and restart because it will think that a thread is stuck. Default debugger timeout is 10 minutes, you may find a log entry saved to disk saying \"Microstack Thread STUCK\", or something similar.

"},{"location":"meshcentral/debugging/#troubleshooting-agent-connectivity","title":"Troubleshooting Agent connectivity","text":"

If an agent keeps disconnecting and reconnecting, add this line to the \"settings\" section of the config.json:

\"agentping\": 30\n

This will cause MeshCentral to \"ping\" the agent every 30 seconds and the agent to respond with a \"pong\" each time. That usually solves the issue however, it does generate more traffic. If that works, you can remove the line and try this line instead:

\"agentpong\": 30\n

This will cause MeshCentral to \"pong\" the agent every 30 seconds, the agent will not respond. This usually fixes the issue, but you have half the traffic. I would also increase the time like:

\"agentpong\": 90\n

This is the best, you have one way traffic to all agents every 90 seconds. The larger the number you can get away with the better.

If you ever get the same problem but on the browser side, you can also use one of these:

\"browserping\": 30\n\"browserpong\": 30\n

Same idea, browser side instead of agent side.

"},{"location":"meshcentral/debugging/#intel-amt","title":"Intel AMT","text":"

To debug issues, confirm that Intel AMT is active and there MeshCentral is not showing any red errors on the \"Intel AMT\" line:

Next, you can go in the \"My Server / Trace\" tab and enable tracing on the \"Intel AMT Manager\" like this:

You can then open another tab and select to power off or power on Intel AMT, you should see \"performPowerAction\" with 2 or 8 depending on power on/off.

"},{"location":"meshcentral/devicetabs/","title":"Device Tabs","text":""},{"location":"meshcentral/devicetabs/#search-or-filter","title":"Search or Filter","text":"

You can search your list of agents using any of these criteria using the filter box (also viewable in the tooltip of the Filter box):

user:xxx or u:xxx ip:xxx group:xxx or g:xxx tag:xxx or t:xxx atag:xxx or a:xxx os:xxx amt:xxx desc:xxx wsc:ok wsc:noav wsc:noupdate wsc:nofirewall wsc:any

"},{"location":"meshcentral/devicetabs/#general","title":"General","text":"

For viewing general information about the agent

  • Group
  • Description
  • IP-KVM Port Number
  • IP-KVM Port Type
  • Intel AMT
  • Intel AMT Tag
  • Mesh Agent
  • Operating System
  • Windows Security
  • Antivirus
  • Active User
  • User Consent
  • Notifications
  • Tags
"},{"location":"meshcentral/devicetabs/#fields","title":"Fields","text":""},{"location":"meshcentral/devicetabs/#buttons","title":"Buttons","text":"

Actions Notes Log Event Message PDU On/Off/Control Chat

"},{"location":"meshcentral/devicetabs/#7-day-power-state","title":"7 Day Power State","text":"

Legend

  • Black color: Device is Powered On on (Intel AMT & agents)
  • Purple color: Device is in sleep state such as Hibernating (Intel AMT agents only)
  • Teal color: Device is connected through AMT/CIRA, but the Power State is UNKNOWN (Intel AMT agents only)
  • DarkGreen color: Device is connected through AMT/CIRA and is in Soft-Off Power State (Intel AMT agents only)
  • Grey color: Device is Powered Off/Not Connected To MeshCentral (Intel AMT & agents)
"},{"location":"meshcentral/devicetabs/#text-links","title":"Text Links","text":"

You can set alternate port in the link if it's not default by right-clicking.

  • Interfaces
  • Location
  • MeshCmd
  • RDP
  • Web-VNC
  • Web-RDP
  • Web-SSH
  • XTerm
  • HTTP
  • HTTPS
  • SSH
  • SCP
  • MQTT Login
"},{"location":"meshcentral/devicetabs/#desktop","title":"Desktop","text":"

For connecting to the machines KVM interface.

"},{"location":"meshcentral/devicetabs/#connect-button","title":"Connect Button","text":"

Right-clicking on Connect button will give you additional options:

  • Ask Consent + Bar
  • Ask Consent
  • Privacy Bar

"},{"location":"meshcentral/devicetabs/#rdp-connect-button","title":"RDP Connect Button","text":"

Right-clicking on RDP Connect button allows you to specify Alternate Port.

"},{"location":"meshcentral/devicetabs/#intel-amt-connect-button","title":"Intel AMT Connect Button","text":"

Uses Intel AMT to control video output of the hardware video card.

"},{"location":"meshcentral/devicetabs/#during-a-desktop-session","title":"During a desktop session","text":"

Bottom left include:

  • Sending special Keys

Top Right include:

Actions

  • Wake Up
  • Run Commands
  • Sleep
  • Reset
  • Power Off
  • Uninstall Agent

Settings

  • Quality
  • Scaling
  • Frame rate
  • Swap Mouse Buttons
  • Reverse Mouse Wheel
  • Use Remote Keyboard Map
  • Automatic Clipboard
  • Lock on Disconnect

  • Session recording indicator
  • Screen rotating
  • switching view mode
  • full screen

Bottom right include:

  • Session Sharing with Guest
  • Toggling keyboard lockout on guest
  • Refresh Desktop view
  • Upload Clipboard
  • Download Clipboard
  • Record Session to file
  • Take screenshot
  • Toggle Remote Desktop Background
  • Open URL on remote desktop
  • Lock the Remote computer
  • Display a notification on the remote computer
  • Open Chat Window
"},{"location":"meshcentral/devicetabs/#terminal","title":"Terminal","text":"

For connecting to a command line based interface on the agent

Right-clicking on Connect button allows you to:

Note

Linux and Windows have different options:

  • Admin Shell (Windows)
  • Admin Powershell (Windows)
  • User Shell (Windows)
  • User Powershell (Windows)
  • SSH (Linux)

"},{"location":"meshcentral/devicetabs/#files","title":"Files","text":"

For transferring files to and from the agent.

"},{"location":"meshcentral/devicetabs/#events","title":"Events","text":"

Mesh Events related to the agent. This is your audit log to see what actions have been taken on the agent from the MeshCentral server.

"},{"location":"meshcentral/devicetabs/#details","title":"Details","text":"

Agent information that includes:

  • OS
  • Agent Info
  • Network Info
  • BIOS
  • Motherboard
  • Memory
  • Storage
  • Intel AMT

Note you can show CPU and Memory usage info by clicking the icon in the top right corner

"},{"location":"meshcentral/devicetabs/#intel-amt","title":"Intel AMT","text":""},{"location":"meshcentral/devicetabs/#console","title":"Console","text":"

For debugging and communicating with the mesh agent.

It allows JS commands to be issued to the device but also run extra commands from the meshcore. Type help for all available options

  • 2falock
  • acceleratorsstats
  • agentissues
  • agentstats
  • amtacm
  • amtmanager
  • amtpasswords
  • amtstats
  • args
  • autobackup
  • backupconfig
  • bad2fa
  • badlogins
  • certexpire
  • certhashes
  • closeusersessions
  • cores
  • dbcounters
  • dbstats
  • dispatchtable
  • dropallcira
  • dupagents
  • email
  • emailnotifications
  • firebase
  • heapdump
  • heapdump2
  • help
  • info
  • le
  • lecheck
  • leevents
  • maintenance
  • migrationagents
  • mps
  • mpsstats
  • msg
  • nodeconfig
  • print
  • relays
  • removeinactivedevices
  • resetserver
  • serverupdate
  • setmaxtasks
  • showpaths
  • sms
  • swarmstats
  • tasklimiter
  • trafficdelta
  • trafficstats
  • updatecheck
  • usersessions
  • versions
  • watchdog
  • webpush
  • webstats
"},{"location":"meshcentral/faq/","title":"FAQ","text":""},{"location":"meshcentral/faq/#json-config-files","title":"json config files","text":"

Any item in the config.json file starting with an underscore character are ignored.

Ignored

\"_title\": \"MyServer\"\n

Valid setting

\"title\": \"MyServer\"\n

json requires correct formatting, if in doubt copy/paste your json config into a web based format checker to make sure you have it right: https://duckduckgo.com/?va=j&t=hc&q=json+lint&ia=answer

"},{"location":"meshcentral/faq/#help-ive-been-hacked-there-are-weird-agents-appearing-in-my-meshcentral-console","title":"Help! I've been hacked there are weird agents appearing in my MeshCentral Console","text":"

No, you haven't.

  1. Your agent installer was scanned by an antivirus.

  2. It didn't recognize the exe.

  3. You have the option enabled to submit unknown applications for analysis.

  4. They ran it against their virtualization testing cluster.

  5. You allow anyone to connect to your server (you should look into techniques to hide your server from the internet).

  6. Here are some examples of what that looks like.

"},{"location":"meshcentral/faq/#cant-login-on-server-after-first-setup","title":"Can't login on server after first setup","text":"

You're sure you're typing in everything right, giving it 2FA code and can't login

TOTP is time sensitive, check your time/NTP and make sure it's right (on server and TOTP app device)! :)

"},{"location":"meshcentral/faq/#branding-and-customization","title":"Branding and Customization","text":"

You can brand and customize MeshCentral almost as much as you like without delving into the code, a few changes in the config.json file and uploading images can change the way your system looks. Read more here

Note

You will need to reinstall the agent for agent customizations to take effect.

"},{"location":"meshcentral/faq/#mac-clients","title":"Mac Clients","text":"

You have to manually grant Mac permissions outside of the agent install process due to the MacOS security system under Security & Privacy > Privacy

To see the screen (otherwise you just see the menu bar, and otherwise blank)

To be able to transfer files

To be able to control keyboard and mouse

"},{"location":"meshcentral/faq/#im-using-cloudflare-and-im-getting-a-black-screen-but-the-mouse-moves","title":"I'm using CloudFlare and I'm getting a black screen but the mouse moves?","text":"

If you are using CloudFlare for your DNS hosting and your remote screen is black, DONT PANIC!

Unfortunately, MeshCentral doesn't always work with CloudFlare's Proxy DNS Mode.

The fix is to simply set the 'Proxy Status' to OFF inside your DNS A Record, within the CloudFlare control panel.

Simply follow the steps here

Once done, open your firewall for the port and agentPort ports of where your meshcentral is hosted, then restart your MeshCentral Server

There is currently a PINNED GitHub issue about this here

"},{"location":"meshcentral/openidConnectStrategy/","title":"Using the OpenID Connect Strategy on MeshCentral","text":""},{"location":"meshcentral/openidConnectStrategy/#overview","title":"Overview","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction","title":"Introduction","text":"

There is a lot of information to go over, but first, why OpenID Connect?

Esentially its because its both based on a industry standard authorization protocol, and is becoming an industry standard authentication protocol. Put simply it's reliable and reusable, and we use OpenID Connect for exactly those reasons, almost every everyone does, and we want to be able to integrate with almost anyone. This strategy allows us to expand the potential of MeshCentral through the potential of OpenID Connect.

In this document, we will learn about the OpenID Connect specification at a high level, and then use that information to configure the OpenID Connect strategy for MeshCentral using a generic OpenID Connect compatible IdP. After that we will go over some advanced configurations and then continue by explaining how to use the new presets for popular IdPs, specifically Google or Azure. Then we will explore the configuration and usage of the groups feature.

ATTENTION: As of MeshCentral v1.1.22 there are multiple config options being depreciated. Using any of the old configs will only generate a warning in the authlog and will not stop you from using this strategy at this time. If there is information found in both the new and old config locations the new config location will be used. We will go over the specifics later, now lets jump in.

"},{"location":"meshcentral/openidConnectStrategy/#chart-of-frequently-used-terms-and-acronyms","title":"Chart of Frequently Used Terms and Acronyms","text":"Term AKA Descriptions OAuth 2.0 OAuth2 OAuth 2.0 is the industry-standard protocol for user authorization. OpenID Connect OIDC Identity layer built on top of OAuth2 for user authentication. Identity Provider IdP The service used to provide authentication and authorization. Preset Configs Presets Set of pre-configured values to allow some specific IdPs to connect correctly. OAuth2 Scope Scope A flag requesting access to a specific resource or endpoint OIDC Claim Claim A returned property in the user info provided by your IdP User Authentication AuthN Checks if you are who you say you are. Example: Username and password authentication User Authorization AuthZ Check if you have the permissions required to access a specific resource or endpoint"},{"location":"meshcentral/openidConnectStrategy/#openid-connect-technology-overview","title":"OpenID Connect Technology Overview","text":"

OpenID Connect is a simple identity layer built on top of the OAuth2 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an \u201cAuthorization Server\u201d, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.

OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and logout, when it makes sense for them.

That description was straight from OpenID Connect Documentation, but basically, OAuth2 is the foundation upon which OpenID Connect was built, allowing for wide ranging compatability and interconnection. OpenID Connect appends the secure user authentication OAuth2 is known for, with user authorization by allowing the request of additional scopes that provide additional claims or access to API's in an easily expandable way.

"},{"location":"meshcentral/openidConnectStrategy/#annotations","title":"Annotations","text":""},{"location":"meshcentral/openidConnectStrategy/#own-idp-ca-and-docker","title":"Own IDP, CA and Docker","text":"

If you operate your own identity provider, your own certification authority and MeshCentral via Docker, it is necessary to provide the complete certificate chain, otherwise NodeJS (in particular the openid-client module) will refuse the connection to the IDP server.

The following errors can be found in the log file:

OIDC: Discovery failed.

UNABLE_TO_GET_ISSUER_CERT_LOCALLY

To solve this problem, the certificate chain in PEM format must be placed in the data directory and the following entry must be added to the docker-compose.yml file in the \u201cenvironment\u201d section:

    environment:\n            - NODE_EXTRA_CA_CERTS=/opt/meshcentral/meshcentral-data/chain.pem\n

"},{"location":"meshcentral/openidConnectStrategy/#basic-config","title":"Basic Config","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction_1","title":"Introduction","text":"

Generally, if you are using an IdP that supports OIDC, you can use a very basic configuration to get started, and if needed, add more specific or advanced configurations later. Here is what your config file will look like with a basic, generic, configuration.

"},{"location":"meshcentral/openidConnectStrategy/#basic-config-file-example","title":"Basic Config File Example","text":"
{\n    \"settings\": {\n        \"cert\": \"mesh.your.domain\",\n        \"port\": 443,\n        \"sqlite3\": true\n    },\n    \"domains\": {\n        \"\": {\n            \"title\": \"MeshCentral\",\n            \"title2\": \"Your sub-title\",\n            \"authStrategies\": {\n                \"oidc\": {\n                    \"issuer\": \"https://sso.your.domain\",\n                    \"clientid\": \"2d5685c5-0f32-4c1f-9f09-c60e0dbc948a\",\n                    \"clientsecret\": \"7PiGSLSLL4e7NGi67KM229tfK7Z7TqzQ\",\n                    \"newAccounts\": true\n                }\n            }\n        }\n    }\n}\n

As you can see, this is roughly the same as all the other OAuth2 based authentication strategies. These are the basics you need to get started, however, if you plan to take advantage of some of the more advanced features provided by this strategy, you'll need to keep reading.

In this most basic of setups, you only need the URL of the issuer, as well as a client ID and a client secret. Notice in this example that the callback URL (or client redirect uri) is not configured, thats because MeshCentral will use https://mesh.your.domain/auth-oidc-callback as the default. Once you've got your configuration saved, restart MeshCentral and you should see an OpenID Connect Single Sign-on button on the login screen.

WARNING: The redirect endpoint must EXACTLY match the value provided to your IdP or your will deny the connection.

ATTENTION: You are required to configure the cert property in the settings section for the default domain, and configure the dns property under each additional domain.

"},{"location":"meshcentral/openidConnectStrategy/#advanced-options","title":"Advanced Options","text":""},{"location":"meshcentral/openidConnectStrategy/#overview_1","title":"Overview","text":"

There are plenty of options at your disposal if you need them. In fact, you can configure any property that node-openid-client supports. The openid-client module supports far more customization than I know what to do with, if you want to know more check out node-openid-client on GitHub for expert level configuration details. There are plenty of things you can configure with this strategy and there is a lot of decumentation behind the tools used to make this all happen. I strongly recommend you explore the config schema, and if you have a complicated config maybe check out the openid-client readme. Theres a list of resources at the end if you want more information on any specific topics. In the meantime, let\u2019s take a look at an example of what your config file could look with a slightly more complicated configuration, including multiple manually defined endpoints.

"},{"location":"meshcentral/openidConnectStrategy/#advanced-config-file-example","title":"Advanced Config File Example","text":"
{\n    \"settings\": {\n        \"cert\": \"mesh.your.domain\",\n        \"port\": 443,\n        \"redirPort\": 80,\n        \"AgentPong\": 300,\n        \"TLSOffload\": \"192.168.1.50\",\n        \"SelfUpdate\": false,\n        \"AllowFraming\": false,\n        \"sqlite3\": true,\n        \"WebRTC\": true\n    },\n    \"domains\": {\n        \"\": {\n            \"title\": \"Mesh\",\n            \"title2\": \".Your.Domain\",\n            \"orphanAgentUser\": \"~oidc:e48f8ef3-a9cb-4c84-b6d1-fb7d294e963c\",\n            \"authStrategies\": {\n                \"oidc\": {\n                    \"issuer\": {\n                        \"issuer\": \"https://sso.your.domain\",\n                        \"authorization_endpoint\": \"https://auth.your.domain/auth-endpoint\",\n                        \"token_endpoint\": \"https://tokens.sso.your.domain/token-endpoint\",\n                        \"end_session_endpoint\": \"https://sso.your.domain/logout\",\n                        \"jwks_uri\": \"https://sso.your.domain/jwks-uri\"\n                    },\n                    \"client\": {\n                        \"client_id\": \"110d5612-0822-4449-a057-8a0dbe26eca5\",\n                        \"client_secret\": \"4TqST46K53o3Z2Q88p39YwR6YwJb7Cka\",\n                        \"redirect_uri\": \"https://mesh.your.domain/auth-oidc-callback\",\n                        \"post_logout_redirect_uri\": \"https://mesh.your.domain/login\",\n                        \"token_endpoint_auth_method\": \"client_secret_post\",\n                        \"response_types\": \"authorization_code\"\n                    },\n                    \"custom\": {\n                        \"scope\": [ \"openid\", \"profile\", \"read.EmailAlias\", \"read.UserProfile\" ],\n                        \"preset\": null\n                    },\n                    \"groups\": {\n                        \"recursive\": true,\n                        \"required\": [\"Group1\", \"Group2\"],\n                        \"siteadmin\": [\"GroupA\", \"GroupB\"],\n                        \"revokeAdmin\": true,\n                        \"sync\": { \n                            \"filter\": [\"Group1\", \"GroupB\", \"OtherGroup\"]\n                        },\n                        \"claim\": \"GroupClaim\",\n                        \"scope\": \"read.GroupMemberships\"\n                    },\n                    \"logouturl\": \"https://sso.your.domain/logout?r=https://mesh.your.domain/login\",\n                    \"newAccounts\": true\n                },\n                {...}\n            }\n        }\n    }\n}\n
"},{"location":"meshcentral/openidConnectStrategy/#issuer-options","title":"\"Issuer\" Options","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction_2","title":"Introduction","text":"

In the advanced example config above, did you notice that the issuer property has changed from a string to an object compared to the basic example? This not only allows for much a much smaller config footprint when advanced issuer options are not required, it successfully fools you in to a false sense of confidence early on in this document. If you are manually configuring the issuer endpoints, keep in mind that MeshCentral will still attempt to discover ALL issuer information. Obviously if you manually configure an endpoint, it will be used even if the discovered information is different from your config.

NOTE: If you are using a preset, you dont need to define an issuer. If you do, the predefined information will be ignored.

"},{"location":"meshcentral/openidConnectStrategy/#common-config-chart","title":"Common Config Chart","text":"Name Description Default Example Required issuer The primary URI that represents your Identity Providers authentication endpoints. N/A \"issuer\": \"https://sso.your.domain\"\"issuer\": { \"issuer\": \"https://sso.your.domain\" } Unless using preset."},{"location":"meshcentral/openidConnectStrategy/#advanced-config-example","title":"Advanced Config Example","text":"
\"issuer\": {\n   \"issuer\": \"https://sso.your.domain\",\n   \"authorization_endpoint\": \"https://auth.your.domain/auth-endpoint\",\n   \"token_endpoint\": \"https://tokens.sso.your.domain/token-endpoint\",\n   \"end_session_endpoint\": \"https://sso.your.domain/logout\",\n   \"jwks_uri\": \"https://sso.your.domain/jwks-uri\"\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#required-and-commonly-used-configs","title":"Required and Commonly Used Configs","text":"

The issuer property in the issuer object is the only one required, and its only required if you aren't using a preset. Besides the issuer, these are mostly options related to the endpoints and their configuration. The schema below looks intimidating but it comes down to being able to support any IdP. Setting the issuer, and end_session_endpoint are the two main ones you want to setup.

"},{"location":"meshcentral/openidConnectStrategy/#schema","title":"Schema","text":"
\"issuer\": { \n    \"type\": [\"string\",\"object\"],\n    \"format\": \"uri\",\n    \"description\": \"Issuer options. Requires issuer URI (issuer.issuer) to discover missing information unless using preset\",\n    \"properties\": {\n        \"issuer\": { \"type\": \"string\", \"format\": \"uri\", \"description\": \"URI of the issuer.\" },\n        \"authorization_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n        \"token_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n        \"jwks_uri\": { \"type\": \"string\", \"format\": \"uri\" },\n        \"userinfo_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n        \"revocation_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n        \"introspection_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n        \"end_session_endpoint\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"description\": \"URI to direct users to when logging out of MeshCentral.\",\n            \"default\": \"this.issuer/logout\"\n            },\n        \"registration_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n        \"token_endpoint_auth_methods_supported\": { \"type\": \"string\" },\n        \"token_endpoint_auth_signing_alg_values_supported\": { \"type\": \"string\" },\n        \"introspection_endpoint_auth_methods_supported\": { \"type\": \"string\" },\n        \"introspection_endpoint_auth_signing_alg_values_supported\": { \"type\": \"string\" },\n        \"revocation_endpoint_auth_methods_supported\": { \"type\": \"string\" },\n        \"revocation_endpoint_auth_signing_alg_values_supported\": { \"type\": \"string\" },\n        \"request_object_signing_alg_values_supported\": { \"type\": \"string\" },\n        \"mtls_endpoint_aliases\": {\n            \"type\":\"object\",\n            \"properties\": {\n                \"token_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n                \"userinfo_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n                \"revocation_endpoint\": { \"type\": \"string\", \"format\": \"uri\" },\n                \"introspection_endpoint\": { \"type\": \"string\", \"format\": \"uri\" }\n            }\n        }\n    },\n    \"additionalProperties\": false\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#client-options","title":"\"Client\" Options","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction_3","title":"Introduction","text":"

There are just about as many option as possible here since openid-client also provides a Client class, because of this you are able to manually configure the client how ever you need. This includes setting your redirect URI to any available path, for example, if I was using the \"google\" preset and wanted to have Google redirect me back to \"https://mesh.your.domain/oauth2/oidc/redirect/givemebackgooglemusicyoujerks\", MeshCentral will now fully support you in that. One of the other options is the post logout redirect URI, and it is exactly what it sounds like. After MeshCentral logs out a user using the IdPs end session endpoint, it send the post logout redirect URI to your IdP to forward the user back to MeshCentral or to an valid URI such as a homepage.

NOTE: The client object is required, however an exception would be with using old configs, which will be discussed later.

"},{"location":"meshcentral/openidConnectStrategy/#common-configs","title":"Common Configs","text":"Name Description Default Example Required client_id The client ID provided by your Identity Provider (IdP) N/A bdd6aa4b-d2a2-4ceb-96d3-b3e23cd17678 true client_secret The client secret provided by your Identity Provider (IdP) N/A vUg82LJ322rp2bvdzuVRh3dPn3oVo29m true redirect_uri \"URI your IdP sends you after successful authorization. https://mesh.your.domain/auth-oidc-callback https://mesh.your.domain/oauth2/oidc/redirect false post_logout_redirect_uri URI for your IdP to send you after logging out of IdP via MeshCentral. https://mesh.your.domain/login https://site.your.other.domain/login false"},{"location":"meshcentral/openidConnectStrategy/#advanced-config-example_1","title":"Advanced Config Example","text":"
\"client\": {\n    \"client_id\": \"00b3875c-8d82-4238-a8ef-25303fa7f9f2\",\n    \"client_secret\": \"7PP453H577xbFDCqG8nYEJg8M3u8GT8F\",\n    \"redirect_uri\": \"https://mesh.your.domain/auth-oidc-callback\",\n    \"post_logout_redirect_uri\": \"https://mesh.your.domain/login\",\n    \"token_endpoint_auth_method\": \"client_secret_post\",\n    \"response_types\": \"authorization_code\"\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#required-and-commonly-used-configs_1","title":"Required and Commonly Used Configs","text":"

There are many available options you can configure but most of them go unused. Although there are a few commonly used properties. The first two properties, client_id and client_secret are required. The next one redirect_uri is used to setup a custom URI for the redirect back to MeshCentral after being authenicated by your IdP. The post_logout_redirect_uri property is used to tell your IdP where to send you after being logged out. These work in conjunction with the issuers end_session_url to automatically fill in any blanks in the config.

"},{"location":"meshcentral/openidConnectStrategy/#schema_1","title":"Schema","text":"
\"client\": { \n    \"type\": \"object\",\n    \"description\": \"OIDC Client Options\",\n    \"properties\": {\n        \"client_id\": { \n            \"type\": \"string\",\n            \"description\": \"REQUIRED: The client ID provided by your Identity Provider (IdP)\"\n        },\n        \"client_secret\": {\n            \"type\": \"string\",\n            \"description\": \"REQUIRED: The client secret provided by your Identity Provider (IdP)\"\n        },\n        \"redirect_uri\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"description\": \"URI your IdP sends you after successful authorization. This must match what is listed with your IdP. (Default is https://[currentHost][currentPath]/auth-oidc-callback)\"\n        },\n        \"post_logout_redirect_uri\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"description\": \"URI for your IdP to send you after logging out of IdP via MeshCentral.\",\n            \"default\": \"https:[currentHost][currentPath]/login\"\n        },\n        \"id_token_signed_response_alg\": { \"type\": \"string\", \"default\": \"RS256\" },\n        \"id_token_encrypted_response_alg\": { \"type\": \"string\" },\n        \"id_token_encrypted_response_enc\": { \"type\": \"string\" },\n        \"userinfo_signed_response_alg\": { \"type\": \"string\" },\n        \"userinfo_encrypted_response_alg\": { \"type\": \"string\" },\n        \"userinfo_encrypted_response_enc\": { \"type\": \"string\" },\n        \"response_types\": { \"type\": [\"string\", \"array\"], \"default\": [\"code\"] },\n        \"default_max_age\": { \"type\": \"number\" },\n        \"require_auth_time\": { \"type\": \"boolean\", \"default\": false }, \n        \"request_object_signing_alg\": { \"type\": \"string\" },\n        \"request_object_encryption_alg\": { \"type\": \"string\" },\n        \"request_object_encryption_enc\": { \"type\": \"string\" },\n        \"token_endpoint_auth_method\": {\n            \"type\": \"string\",\n            \"default\": \"client_secret_basic\",\n            \"enum\": [ \"none\", \"client_secret_basic\", \"client_secret_post\", \"client_secret_jwt\", \"private_key_jwt\" ]\n        }, \n        \"introspection_endpoint_auth_method\": {\n            \"type\": \"string\",\n            \"default\": \"client_secret_basic\",\n            \"enum\": [ \"none\", \"client_secret_basic\", \"client_secret_post\", \"client_secret_jwt\", \"private_key_jwt\" ]\n        }, \n        \"revocation_endpoint_auth_method\": {\n            \"type\": \"string\",\n            \"default\": \"client_secret_basic\",\n            \"enum\": [ \"none\", \"client_secret_basic\", \"client_secret_post\", \"client_secret_jwt\", \"private_key_jwt\" ]\n        }, \n        \"token_endpoint_auth_signing_alg\": { \"type\": \"string\" },\n        \"introspection_endpoint_auth_signing_alg\": { \"type\": \"string\" },\n        \"revocation_endpoint_auth_signing_alg\": { \"type\": \"string\" },\n        \"tls_client_certificate_bound_access_tokens\": { \"type\": \"boolean\" }\n    },\n    \"required\": [ \"client_id\", \"client_secret\" ],\n    \"additionalProperties\": false\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#custom-options","title":"\"Custom\" Options","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction_4","title":"Introduction","text":"

These are all the options that dont fit with the issuer or client, including the presets. The presets define more than just the issuer URL used in discovery, they also define API endpoints, and specific ways to assemble your data. You are able to manually override most of the effects of the preset, but not all. You are able to manually configure the scope of the authorization request though, as well as choose which claims to use if your IdP uses something other than the defaults.

NOTE: The scope must be a string, an array of strings, or a space separated list of scopes as a single string.

"},{"location":"meshcentral/openidConnectStrategy/#common-config-chart_1","title":"Common Config Chart","text":"Name Description Default Example Required scope A list of scopes to request from the issuer. \"openid profile email\" [\"openid\", \"profile\"] false claims A group of claims to use instead of the defaults Defauts to name of property except that uuid used sub \"claims\": {\"uuid\": \"unique_name\"} false"},{"location":"meshcentral/openidConnectStrategy/#advanced-config-example_2","title":"Advanced Config Example","text":"
\"custom\": {\n    \"scope\": [ \"openid\", \"profile\", \"read.EmailAlias\", \"read.UserProfile\" ],\n    \"preset\": null,\n    \"claims\": {\n        \"name\": \"nameOfUser\",\n        \"email\": \"publicEmail\"\n    }\n},\n

NOTE: You can preset to null if you want to explicitly disable presets.

"},{"location":"meshcentral/openidConnectStrategy/#required-and-commonly-used-configs_2","title":"Required and Commonly Used Configs","text":"

As should be apparent by the name alone, the custom property does not need to be configured and is used for optional or advanced configurations. With that said, lets look at few common options strategy will default to using the openid, profile, and email scopes to gather the required information about the user, if your IdP doesn't support or require all these, you can set up the scope manually. Combine that with the ability to set the group scope and you can end up with an entirely custom scope being sent to your IdP. Not to mention the claims property, which allows you to pick and choose what claims to use to gather your data in case you have issues with any of the default behaviors of OpenID Connect and your IdP. This is also where you would set the preset and any values required by the presets.

"},{"location":"meshcentral/openidConnectStrategy/#schema_2","title":"Schema","text":"
\"custom\": {\n    \"type\": \"object\",\n    \"properties\": {\n        \"scope\": {\n            \"type\": [\"string\", \"array\"],\n            \"description\": \"A list of scopes to request from the issuer.\",\n            \"default\": \"openid profile email\",\n            \"examples\": [\"openid\", [\"openid\", \"profile\"], \"openid profile email\", \"openid profile email groups\"]\n        },\n        \"claims\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"email\": { \"type\": \"string\" },\n                \"name\": { \"type\": \"string\" },\n                \"uuid\": { \"type\": \"string\" }\n            }\n        },\n        \"preset\": { \"type\": \"string\", \"enum\": [\"azure\", \"google\"]},\n        \"tenant_id\": { \"type\": \"string\", \"description\": \"REQUIRED FOR AZURE PRESET: Tenantid for Azure\"},\n        \"customer_id\": { \"type\": \"string\", \"description\": \"REQUIRED FOR GOOGLE PRESET IF USING GROUPS: Customer ID from Google, should start with 'C'.\"}\n    },\n    \"additionalProperties\": false\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#groups-options","title":"\"Groups\" Options","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction_5","title":"Introduction","text":"

The groups option allows you to use the groups you already have with your IdP in MeshCentral in a few ways. First you can set a group that the authorized user must be in to sign in to MeshCentral. You can also allow users with the right memberships automatic admin privlidges, and there is even an option to revoke privlidges if the user is NOT in the admin group. Besides these filters, you can filter the sync property to mirror only certain groups as MeshCentral User Groups, dynamically created as the user logs in. You can of course simply enable sync and mirror all groups from your IdP as User Groups. Additionally you can define the scope and claim of the groups for a custom setup, again allowing for a wide range of IdPs to be used, even without a preset.

"},{"location":"meshcentral/openidConnectStrategy/#common-config-chart_2","title":"Common Config Chart","text":"Name Description Default Example Required sync Allows you to mirror user groups from your IdP. false \"sync\": { \"filter\": [\"Group1\", \"Group2\"] }\"sync\": true false required Access is only granted to users who are a memberof at least one of the listed required groups. undefined \"required\": [\"Group1\", \"Group2\"] false siteadmin Full site admin priviledges will be granted to userswho are a member of at least one of the listed admin groups undefined \"siteadmin\": [\"Group1\", \"Group2\"] false revokeAdmin If true, admin privileges will be revoked from userswho arent a member of at least one of the listed admin groups. true \"revokeAdmin\": false false"},{"location":"meshcentral/openidConnectStrategy/#advanced-config-example_3","title":"Advanced Config Example","text":"
\"groups\": {\n    \"recursive\": true,\n    \"required\": [\"Group1\", \"Group2\"],\n    \"siteadmin\": [\"GroupA\", \"GroupB\"],\n    \"revokeAdmin\": false,\n    \"sync\": { \n        \"filter\": [\"Group1\", \"GroupB\", \"OtherGroup\"]\n    },\n    \"claim\": \"GroupClaim\",\n    \"scope\": \"read.GroupMemberships\"\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#required-and-commonly-used-configs_3","title":"Required and Commonly Used Configs","text":"

As you can see in the schema below, there aren't any required properties in the groups object, however there are some commonly used ones. The first, and maybe most commonly used one, is the sync property. The sync property mirrors IdP provided groups into MeshCentral as user groups. You can then configure access as required to those groups, and as users log in, they will be added to the now existing groups if they are a member. You also have other options like using a custom scope or claim to get your IdP communicating with MeshCentral properly, without the use of preset configs. You also can set the required property if you need to limit authorization to users that are a member of at least one of the groups you set. or the siteadmin property to grant admin privilege, with the revokeAdmin property available to allow revoking admin rights also.

"},{"location":"meshcentral/openidConnectStrategy/#schema_3","title":"Schema","text":"
\"groups\": {\n  \"type\": \"object\",\n  \"properties\": {\n    \"recursive\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"description\": \"When true, the group memberships will be scanned recursively.\"\n    },\n    \"required\": {\n      \"type\": [ \"string\", \"array\" ],\n      \"description\": \"Access is only granted to users who are a member of at least one of the listed required groups.\"\n    },\n    \"siteadmin\": {\n      \"type\": [ \"string\", \"array\" ],\n      \"description\": \"Full site admin priviledges will be granted to users who are a member of at least one of the listed admin groups.\"\n    },\n    \"revokeAdmin\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"description\": \"If true, admin privileges will be revoked from users who are NOT a member of at least one of the listed admin groups.\"\n    },\n    \"sync\": {\n      \"type\": [ \"boolean\", \"object\" ],\n      \"default\": false,\n      \"description\": \"If true, all groups found during user login are mirrored into MeshCentral user groups.\",\n      \"properties\": {\n        \"filter\": {\n          \"type\": [ \"string\", \"array\" ],\n          \"description\": \"Only groups listed here are mirrored into MeshCentral user groups.\"\n        }\n      }\n    },\n    \"scope\": { \"type\": \"string\", \"default\": \"groups\", \"description\": \"Custom scope to use.\" },\n    \"claim\": { \"type\": \"string\", \"default\": \"groups\", \"description\": \"Custom claim to use.\" }\n  },\n  \"additionalProperties\": false\n}\n
"},{"location":"meshcentral/openidConnectStrategy/#preset-openid-connect-configurations","title":"Preset OpenID Connect Configurations","text":""},{"location":"meshcentral/openidConnectStrategy/#overview_2","title":"Overview","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction_6","title":"Introduction","text":"

Google is a blah and is used by tons of blahs as its so great. Lets move on.

"},{"location":"meshcentral/openidConnectStrategy/#common-config-chart_3","title":"Common Config Chart","text":"

NOTE: All settings directly related to presets are in the custom section of the config.

Name Description Example Required preset Manually enable the use of a preset. \"preset\": \"google\"\"preset\": \"azure\" false customer_id Customer ID of the Google Workspaces instace youplan to use with the groups feature. \"customer_id\": [\"Group1\", \"Group2\"] If google preset is used with groups feature tenant_id Tenant ID from Azure AD, this is required to usethe azure preset as it is part of the issuer url. \"siteadmin\": [\"Group1\", \"Group2\"] false"},{"location":"meshcentral/openidConnectStrategy/#google-preset","title":"Google Preset","text":""},{"location":"meshcentral/openidConnectStrategy/#prerequisites","title":"Prerequisites","text":"

Check out this documentation to get ready before we start.

"},{"location":"meshcentral/openidConnectStrategy/#basic-config-example","title":"Basic Config Example","text":"
\"oidc\": {\n    \"client\": {\n        \"client_id\": \"268438852161-r8xa7qxwf3rr0shp1xnpgmm70bnag21p.apps.googleusercontent.com\",\n        \"client_secret\": \"ETFWBX-gFEaxfPXs1tWmAOkuWDFTgoL3nwh\"\n    }\n}\n
"},{"location":"meshcentral/openidConnectStrategy/#specifics","title":"Specifics","text":"

If you notice above I forgot to add any preset related configs, however because google tags the client ID we can detect that and automatically use the google preset. The above config is tested, the sentive data has been scrambled of course. That said, you would normally use this preset in more advaced setups, let take a look at an example.

"},{"location":"meshcentral/openidConnectStrategy/#advanced-example-with-groups","title":"Advanced Example with Groups","text":"
\"oidc\": {\n    \"client\": {\n        \"client_id\": \"424555768625-k7ub3ovqs0yp7mfo0usvyyx51nfii61c.apps.googleusercontent.com\",\n        \"client_secret\": \"QLBCQY-nRYmjnFWv3nKyHGmwQEGLokP6ldk\"\n    },\n    \"custom\": {\n        \"preset\": \"google\",\n        \"customer_id\": \"C46kyhmps\"\n    },\n    \"groups\": {\n        \"siteadmin\": [\"GroupA\", \"GroupB\"],\n        \"revokeAdmin\": true,\n        \"sync\": true\n    },\n    \"callbackURL\": \"https://mesh.your.domain/auth-oidc-google-callback\"\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#customer-id-and-groups","title":"Customer ID and Groups","text":"

As always, the client ID and secret are required, the customer ID on the other hand is only required if you plan to take advantage of the groups function and the google preset. This also requires you have a customer ID, if you have do, it is available in the Google Workspace Admin Console under Profile->View. Groups work the same as they would with any other IdP but they are pulled from the Workspace groups.

"},{"location":"meshcentral/openidConnectStrategy/#schema_4","title":"Schema","text":"
\"custom\": {\n    \"type\": \"object\",\n    \"properties\": {\n        \"preset\": { \"type\": \"string\", \"enum\": [\"azure\", \"google\"]},\n        \"customer_id\": { \"type\": \"string\", \"description\": \"Customer ID from Google, should start with 'C'.\"}\n    },\n    \"additionalProperties\": false\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#azure-preset","title":"Azure Preset","text":""},{"location":"meshcentral/openidConnectStrategy/#prerequisites_1","title":"Prerequisites","text":"

To configure OIDC-based SSO, you need an Azure account with an active subscription. Create an account for free. The account used for setup must be of the following roles: Global Administrator, Cloud Application Administrator, Application Administrator, or owner the service principal.

Check this documentation for more information.

"},{"location":"meshcentral/openidConnectStrategy/#basic-config-example_1","title":"Basic Config Example","text":"
\"oidc\": {\n    \"client\": {\n        \"client_id\": \"a1gkl04i-40g8-2h74-6v41-2jm2o2x0x27r\",\n        \"client_secret\": \"AxT6U5K4QtcyS6gF48gndL7Ys22BL15BWJImuq1O\"\n    },\n    \"custom\": {\n        \"preset\": \"azure\",\n        \"tenant_id\": \"46a6022g-4h33-1451-h1rc-08102ga3b5e4\"\n    }\n}\n
"},{"location":"meshcentral/openidConnectStrategy/#specifics_1","title":"Specifics","text":"

As with all other types of configuration for the OIDC strategy, the Azure preset requires a client ID and secret.The tenant ID is used as part of the issuer URI to make even the most basic AuthN requests so it is also required for the azure preset. besides that groups are available to the Azure preset as well as the recursive feature of groups. This allows you to search user groups recursively for groups they have membership in through other groups.

NOTE: The Azure AD preset uses the Tenant ID as part of the issuer URI:\"https://login.microsoftonline.com/\" + strategy.custom.tenant_id + \"/v2.0\"

"},{"location":"meshcentral/openidConnectStrategy/#advanced-example-with-groups_1","title":"Advanced Example with Groups","text":"
\"oidc\": {\n    \"client\": {\n        \"client_id\": \"a1gkl04i-40g8-2h74-6v41-2jm2o2x0x27r\",\n        \"client_secret\": \"AxT6U5K4QtcyS6gF48gndL7Ys22BL15BWJImuq1O\"\n    },\n    \"custom\": {\n        \"preset\": \"azure\",\n        \"tenant_id\": \"46a6022g-4h33-1451-h1rc-08102ga3b5e4\"\n    },\n    \"groups\": {\n        \"recursive\": true,\n        \"siteadmin\": [\"GroupA\", \"GroupB\"],\n        \"revokeAdmin\": true,\n        \"sync\": true\n    },\n    \"callbackURL\": \"https://mesh.your.domain/auth-oidc-azure-callback\"\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#schema_5","title":"Schema","text":"
\"custom\": {\n    \"type\": \"object\",\n    \"properties\": {\n        \"preset\": { \"type\": \"string\", \"enum\": [\"azure\", \"google\"]},\n        \"tenant_id\": { \"type\": \"string\", \"description\": \"Tenant ID from Azure AD.\"}\n    },\n    \"additionalProperties\": false\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#depreciated-properties","title":"Depreciated Properties","text":""},{"location":"meshcentral/openidConnectStrategy/#overview_3","title":"Overview","text":""},{"location":"meshcentral/openidConnectStrategy/#introduction_7","title":"Introduction","text":"

As of MeshCentral v1.1.22 and the writing of this documentation, the node module that handles everything was changed from passport-openid-connect to openid-client. As a result of this change, multiple properties in the config have been depcrecated; this means some options in the strategy arent being used anymore. These are often referred to as \"old configs\" by this documentation.

"},{"location":"meshcentral/openidConnectStrategy/#migrating-old-configs","title":"Migrating Old Configs","text":"

We upgraded but what about all the existing users, we couldn't just invalidate every config pre v1.1.22. So in an effort to allow greater flexibility to all users of MeshCentral, and what futures scholars will all agree was an obvious move, all the depreciated configs will continue working as expected. Using any of the old options will just generate a warning in the authlog and will not stop you from using this the OIDC strategy with outdated configs, however if both the equivalent new and old config are set the new config will be used.

"},{"location":"meshcentral/openidConnectStrategy/#old-config-example","title":"Old Config Example","text":"
\"oidc\": {\n    \"newAccounts\": true,\n    \"clientid\": \"421326444155-i1tt4bsmk3jm7dri6jldekl86rfpg07r.apps.googleusercontent.com\",\n    \"clientsecret\": \"GNLXOL-kEDjufOCk6pIcTHtaHFOCgbT4hoi\"\n}\n

This example was chosen because I wanted to highlight an advantage of supporting these old configs long term, even in a depreciated status. That is, the ability to copy your existing config from one of the related strategies without making any changes to your config by using the presets. This allows you to test out the oidc strategy without commiting to anything, since the user is always appended with the strategy used to login. In this example, the config was originally a google auth strategy config, changing the \"google\" to \"oidc\" is all that was done to the above config, besides obsfuscation of course.

"},{"location":"meshcentral/openidConnectStrategy/#advcanced-old-config-example","title":"Advcanced Old Config Example","text":"
\"oidc\": {\n    \"authorizationURL\": \"https://sso.your.domain/api/oidc/authorization\",\n    \"callbackURL\": \"https://mesh.your.domain/oauth2/oidc/callback\",\n    \"clientid\": \"tZiPTMDNuSaQPapAQJtwDXVnYjjhQybc\",\n    \"clientsecret\": \"vrQWspJxdVAxEFJdrxvxeQwWkooVcqdU\",\n    \"issuer\": \"https://sso.your.domain\",\n    \"tokenURL\": \"https://sso.your.domain/api/oidc/token\",\n    \"userInfoURL\": \"https://sso.your.domain/api/oidc/userinfo\",\n    \"logoutURL\": \"https://sso.your.domain/logout?rd=https://mesh.your.domain/login\",\n    \"groups\": {\n        \"recursive\": true,\n        \"required\": [\"Group1\", \"Group2\"],\n        \"siteadmin\": [\"GroupA\", \"GroupB\"],\n        \"sync\": { \n            \"filter\": [\"Group1\", \"GroupB\", \"OtherGroup\"]\n        }\n    },\n    \"newAccounts\": true\n},\n
"},{"location":"meshcentral/openidConnectStrategy/#upgrading-to-v1122","title":"Upgrading to v1.1.22","text":"

If you were already using a meticulusly configured oidc strategy, all of your configs will still be used. You will simply see a warning in the logs if any depreciated properties were used. If you check the authLog there are additional details about the old config and provide the new place to put that information. In this advanced config, even the groups will continue to work just as they did before without any user intervention when upgrading from a version of MeshCentral pre v1.1.22. There are no step to take and no action is needed, moving the configs to the new locations is completely optional at the moment.

"},{"location":"meshcentral/openidConnectStrategy/#links","title":"Links","text":"

https://cloud.google.com/identity/docs/reference/rest/v1/groups/list

https://www.onelogin.com/learn/authentication-vs-authorization

https://auth0.com/docs/authenticate/protocols/openid-connect-protocol

https://github.com/panva/node-openid-client

https://openid.net/connect/

You just read openidConnectStrategy.ms v1.0.1 by @mstrhakr

"},{"location":"meshcentral/plugins/","title":"Plugins - Installation & Usage","text":"

Note

Plugins as such receive no support by the main developers of MeshCentral. If you experience problems with MeshCentral please make sure to disable all plugins before further troubleshooting!

"},{"location":"meshcentral/plugins/#use-cases","title":"Use Cases","text":"

Certain feature requests may not be suitable for all MeshCentral users and thus are available as a plugin. Furthermore users can develop their own plugins - as described further below - to extend functionality or benefit from integrating MeshCentral into their existing application environment.

"},{"location":"meshcentral/plugins/#list-of-publically-available-plugins","title":"List of publically available plugins","text":"

https://github.com/topics/meshcentral-plugin

"},{"location":"meshcentral/plugins/#installation-of-a-plugin","title":"Installation of a plugin","text":"
  1. First please make sure that you enable plugins in the configuration

    \"plugins\": { \"enabled\": true },

  2. Restart MeshCentral if you needed to change the configuration.
  3. Log into MeshCentral as full administrator.
  4. Go my My Server -> Plugins, then hit the Download plugin button.
  5. A dialog opens requesting a URL, e.g. put in: https://github.com/ryanblenis/MeshCentral-ScriptTask
  6. The plugin pops up in the plugin list below the download button, you can now configure and enable/disable it.
"},{"location":"meshcentral/plugins/#plugins-development-hooks","title":"Plugins - Development & Hooks","text":"

Note

Plugins as such receive no support by the main developers of MeshCentral. If you experience problems with MeshCentral please make sure to disable all plugins before further troubleshooting!

"},{"location":"meshcentral/plugins/#overview","title":"Overview","text":"

Not all feature requests may be suitable for all MeshCentral users and thus can't be integrated into MeshCentral directly. Hwoever, Instead of maintaining a complete fork of MeshCentral it is much easier to extend MeshCentral's functionality using hooks and writing plugins for it.

"},{"location":"meshcentral/plugins/#anatomy-of-a-plugin","title":"Anatomy of a plugin:","text":"
- plugin_name/\n-- config.json\n-- plugin_name.js\n-- modules_meshcore/ // optional\n--- plugin_name.js  // optional\n
"},{"location":"meshcentral/plugins/#plugin-configuration-file","title":"Plugin Configuration File","text":"

A valid JSON object within a file named config.json in the root folder of your project. An example:

{\n  \"name\": \"Plugin Name\",\n  \"shortName\": \"plugin_name\",\n  \"version\": \"0.0.0\",\n  \"author\": \"Author Name\",\n  \"description\": \"Short Description of the plugin\",\n  \"hasAdminPanel\": false,\n  \"homepage\": \"https://www.example.com\",\n  \"changelogUrl\": \"https://raw.githubusercontent.com/User/Project/master/changelog.md\",\n  \"configUrl\": \"https://raw.githubusercontent.com/User/Project/master/config.json\",\n  \"downloadUrl\": \"https://github.com/User/Project/archive/master.zip\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/User/Project.git\"\n  },\n  \"versionHistoryUrl\": \"https://api.github.com/repos/User/Project/tags\",\n  \"meshCentralCompat\": \">0.4.3\"\n}\n
"},{"location":"meshcentral/plugins/#configuration-file-properties","title":"Configuration File Properties","text":"Field Required Type Description name Yes string a human-readable name for the plugin shortName Yes string an alphanumeric, unique short identifier for the plugin (will be used to access your functions throughout the project version Yes string the current version of the plugin author No string the author's name description Yes string a short, human-readable description of what the plugin does hasAdminPanel Yes boolean true or false, indicates whether or not the plugin will offer its own administrative interface homepage Yes string the URL of the projects homepage changelogUrl Yes string the URL to the changelog of the project configUrl Yes string the URL to the config.json of the project downloadUrl Yes string the URL to a ZIP of the project (used for installation/upgrades) repository Yes JSON object contains the following attributes repository.type Yes string valid values are git and in the future, npm will also be supported. repository.url Yes string the URL to the project's repository versionHistoryUrl No string the URL to the project's versions/tags meshCentralCompat Yes string the minimum version string of required compatibility with the MeshCentral server, can be formatted as \"0.1.2-c\" or \">=0.1.2-c\". Currently only supports minimum version, not full semantic checking."},{"location":"meshcentral/plugins/#plugin-hooks","title":"Plugin Hooks","text":"

In essence, hooks are locations in the code which enable developers to tap into a module to either provide alternative behavior or to respond to an event.

These are separated into the following categories depending on the type of functionality the plugin should offer.

  • Web UI, to modify the MeshCentral admin interface
  • Back End, to modify core functionality of the server and communicate with the Web UI layer as well as the Mesh Agent (Node) layer to send commands and data
  • Mesh Agent (Node), to introduce functionality to each agent
"},{"location":"meshcentral/plugins/#web-ui-hooks","title":"Web UI Hooks","text":"
  • onDeviceRefreshEnd: called when a device is selected in the MeshCentral web interface
  • registerPluginTab: callable when a device is selected in the MeshCentral web interface to register a new tab for plugin data, if required. Accepts an object, or function that returns an object, with the following properties: { tabId: \"yourShortNameHere\", tabTitle: \"Your Display Name\"}. A tab and div with the associated ID and title will be created for your use
  • onDesktopDisconnect: called when a remote desktop session is disconnected
  • onWebUIStartupEnd: called when the page has loaded for the first time after a login / refresh
  • goPageStart: called before page changes take effect. Passes 2 arguments ( : int, : Event)
  • goPageEnd: called after page changes take effect. Passes 2 arguments ( : int, : Event)"},{"location":"meshcentral/plugins/#exports","title":"Exports","text":"

    Any function can be exported to the Web UI layer by adding the name of the function to an exports array in the plugin object.

    "},{"location":"meshcentral/plugins/#back-end-hooks","title":"Back End Hooks","text":"
    • server_startup: called once when the server starts (or when the plugin is first installed)
    • hook_agentCoreIsStable: called once when an agent initially checks in
    • hook_processAgentData: called each time an agent transmits data back to the server
    • hook_userLoggedIn: called when a user has logged into the web interface
    • hook_setupHttpHandlers: called before all http handlers are setup
    "},{"location":"meshcentral/plugins/#mesh-agent","title":"Mesh Agent","text":"

    Use of the optional file plugin_name.js in the optional folder modules_meshcore will include the file in the default meshcore file sent to each endpoint. This is useful to add functionality on each of the endpoints.

    "},{"location":"meshcentral/plugins/#structure","title":"Structure","text":"

    Much of MeshCentral revolves around returning objects for your structures, and plugins are no different. Within your plugin you can traverse all the way up to the web server and MeshCentral Server classes to access all the functionality those layers provide. This is done by passing the current object to newly created objects, and assigning that reference to a parent variable within that object.

    "},{"location":"meshcentral/plugins/#versioning","title":"Versioning","text":"

    Versioning your plugin correctly and consistently is essential to ensure users of your plugin are prompted to upgrade when it is available. Semantic versioning is recommended.

    "},{"location":"meshcentral/plugins/#changelog","title":"Changelog","text":"

    A changelog is highly recommended so that your users know what's changed since their last version.

    "},{"location":"meshcentral/plugins/#sample-plugin","title":"Sample Plugin","text":"

    MeshCentral-Sample is a simple plugin that, upon disconnecting from remote desktop, prompts the user to enter a manual event (note), pre-filled in with the date and timestamp.

    "},{"location":"meshcentral/security/","title":"Security","text":""},{"location":"meshcentral/security/#rate-limiting-login-attempts","title":"Rate Limiting login attempts","text":"

    You can use the MeshCentral Server Console with the command badlogins to see the current settings.

    Adjust these items in your config.json

    \"settings\": {\n    \"_maxInvalidLogin\": {\n    \"time\": 10,\n    \"count\": 10,\n    \"coolofftime\": 10\n    },\n}\n

    "},{"location":"meshcentral/security/#disabling-tls-1011-for-amt","title":"Disabling TLS 1.0/1.1 for AMT","text":"
    {\n  \"settings\": {\n    \"mpshighsecurity\": true\n  }\n}\n
    "},{"location":"meshcentral/tipsntricks/","title":"Tips n' Tricks","text":""},{"location":"meshcentral/tipsntricks/#colors-in-ssh","title":"Colors in SSH","text":"

    The SSH terminal does support color. The issue is going to be the terminal configuration of the shell. Try typing this:

    ls -al --color /tmp\n
    "},{"location":"meshcentral/tipsntricks/#fancy-config-editing-with-vs-code","title":"Fancy config editing with VS Code","text":"

    A common problem in the issues is an incorrect config.json. What makes a config incorrect? How can you verify your config is correct?

    Easy! Use Visual Studio Code to edit your config.json and add the schema at the top.

    If you haven't already, download VS code. Download or copy the config.json to your computer. Open config.json in code and add the schema as the top line. This schema is the raw JSON file in the MeshCentral repo.

    {\n  \"$schema\": \"https://raw.githubusercontent.com/Ylianst/MeshCentral/master/meshcentral-config-schema.json\",\n  \"settings\": {\n    \"your settings go here\": \"...\"\n  }\n}\n

    Now you have autocomplete, auto-format and validation for your config.json! If you start typing, Code will show the values that are valid for the location you are editing. Words with a red squiggle line are errors. Words with a orange squiggle line are warnings. Hover over both to see the error message and possible fixes. Code can even format your config.

    While this is a huge step up, it's not perfect. If you notice, there are some invalid keys in the screenshot. This is perfectly valid JSON and MeshCentral will ignore them (maybe?). If you paste some configs into the wrong section, code will not tell you it's in the wrong section. Autocomplete will tell you what keys are valid and the type of the value (i.e. string, number, boolean).

    Hopefully this will help verify your config is syntactically correct and prevent needless formatting errors, misspellings, etc.

    "},{"location":"meshcentral/tipsntricks/#downloading-folders","title":"Downloading Folders","text":"

    If you would like to download folders via Files simply select folder/files then use the zip and download the zip file by clicking on it.

    "},{"location":"meshcentral/tipsntricks/#share-device-groups-with-ad-logins","title":"Share device groups with AD logins","text":"

    If you would like to share device groups with different AD users.

    In the config.json set \"ldapuserkey\" to \"sAMAccountName\".

    "},{"location":"meshcentral/tokens/","title":"14.1 Tokens","text":""},{"location":"meshcentral/tokens/#user-tokens","title":"User Tokens","text":""},{"location":"meshcentral/tokens/#software-integration-tokens","title":"Software Integration Tokens","text":"

    Warning

    You can only have a SINGLE loginTokenKey for your meshcentral server! So if you regenerate a loginTokenKey, the old one will be revoked/deleted!

    You can create/view the Login Token Key with the following:

    node node_modules/meshcentral --loginTokenKey\n

    You can then reset/revoke/renew the Login Token Key with the following to create a new one:

    node node_modules/meshcentral --loginTokenKey --loginTokenGen\n
    "},{"location":"meshcmd/","title":"MeshCmd","text":"

    MeshCmd Guide as .pdf as .odt

    "},{"location":"meshcmd/#video-walkthru","title":"Video Walkthru","text":""},{"location":"meshcmd/#abstract","title":"Abstract","text":"

    This user guide contains all essential information for the user to make full use of MeshCmd, a command line tool used to perform tasks on MeshCentral and for Intel\u00ae AMT. This tool run on Windows and Linux and perform a wide array of different tasks. From routing traffic over the Internet to activating Intel AMT, MeshCmd is a great do it all tool for computer management.

    "},{"location":"meshcmd/#introduction","title":"Introduction","text":"

    MeshCmd, called \u201cMesh Command\u201d, is a command line tool that runs on both Windows and Linux and used to perform many tasks related to computer management. As the tool continues to evolve, it will continue to be improved and acquire more features. Broadly, the tool is intended to perform three sets of tasks.

    MeshCentral2 command line operations - There are command line operations that relate to interacting with the MeshCentral2 server. A good example of this is to route traffic from your computer to a remote computer on the internet thru a MeshCentral2 server.

    Intel AMT local actions - If you happen to have Intel AMT on your computer, MeshCmd can take a look at the version and activation status, activate and de-activate Intel AMT and help with getting access to Intel AMT and more.

    Intel AMT remote actions - Whether you have Intel AMT on your local computer or a remote computer on your network, MeshCmd can help unlock the features Intel AMT provides. From getting the state of Intel AMT remotely to running configuration scripts and loading MeshCommander into Intel AMT web storage.

    To get started, you need to download MeshCmd for your computer. MeshCmd is a single file executable that you can get on MeshCommander.com at: http://www.meshcommander.com/meshcommander/meshcmd

    If you have access to a MeshCentral2 server, the download link to MeshCmd is at the bottom left of the main device page.

    Once you click on the \u201cMeshCmd\u201d link, a dialog box will allow you to select the operating system you want to get a link to MeshCmd. MeshCentral will also provide an action file called meshaction.txt that contains information on how MeshCmd can connect back to that MeshCentral server. This is optional, and only used for some operations.

    Once downloaded, just run it from the command prompt or terminal window.

    C:\\Temp>meshcmd\nMeshCentral Command (MeshCmd)\nNo action specified, use MeshCmd like this:\n\n  meshcmd [action] [arguments...]\n\nValid MeshCentral actions:\n  Route             - Map a local TCP port to a remote computer.\n\nValid local actions:\n  SMBios            - Display System Management BIOS tables for this computer.\n  RawSMBios         - Display RAW System Management BIOS tables for this computer.\n  MicroLMS          - Run MicroLMS, allowing local access to Intel AMT.\n  AmtInfo           - Show Intel AMT version and activation state.\n  AmtVersions       - Show all Intel ME version information.\n  AmtHashes         - Show all Intel AMT trusted activation hashes.\n  AmtCCM            - Activate Intel AMT into Client Control Mode.\n  AmtACM            - Activate Intel AMT into Admin Control Mode.\n  AmtDeactivate     - Deactivate Intel AMT if activated in Client Control mode.\n  AmtAcmDeactivate  - Deactivate Intel AMT if activated in Admin Control mode.\n\nValid local or remote actions:\n  MeshCommander     - Launch a local MeshCommander web server.\n  AmtUUID           - Show Intel AMT unique identifier.\n  AmtAuditLog       - Show the Intel AMT audit log.\n  AmtLoadWebApp     - Load MeshCommander in Intel AMT 11.6+ firmware.\n  AmtClearWebApp    - Clear everything from Intel AMT web storage.\n  AmtStorageState   - Show contents of the Intel AMT web storage.\n  AmtSaveState      - Save all Intel AMT WSMAN object to file.\n  AmtPresence       - Heartbeat a local Intel AMT watchdog agent.\n  AmtScript         - Run .mescript on Intel AMT.\n  AmtIDER           - Mount local disk image to remote computer.\n\nHelp on a specific action using:\n\n  meshcmd help [action]\n

    By default you will get the help screen with all of the different actions you can take with the tool. You can also get help by typing \u201chelp\u201d followed by the action name. In this document we will cover the main actions that MeshCmd can perform. If you don\u2019t use Intel AMT at all, then only the MeshCentral actions are interesting for you. If you use Intel AMT, the rest of the actions will be of interest to you.

    "},{"location":"meshcmd/#meshcentral-tcp-port-mapping","title":"MeshCentral TCP port mapping","text":"

    MeshCmd can map a TCP port from your local computer to any remote port on any computer with one of your MeshAgents installed. This port mapping will work over a local network or the Internet and should work even thru proxies and firewalls.

    In order to start using MeshCmd in this way, you first need to have access to a MeshCentral server and at least one computer you already manage. TCP port mapping works by selecting a port on your local computer that will be routed to a remote device and port. Here, local port 123 is routed thru the server to port 123 on a remote device.

    Of course, this picture is a bit simplify. Firewalls, NAT routers and HTTP proxies may be in the way and the MeshAgent on the remote computer will act at the TCP traffic relay in most cases.

    One typical use of this is to route local port 1234 to port Microsoft RDP port 3389 on a remote device. Once routed, you can start a RDP session on \u201clocalhost:1234\u201d and get a RDP session to the remote device. The RDP protocol is feature rich and efficient, so you get a great user experience regardless of where in the world the remote computer is at.

    To get started, click on a device in MeshCentral and click on the \u201cRouter\u201d link on the bottom left of the device page.

    You can download MeshCmd is you have not done so already, but more importantly, download the action.txt file. The file is in text format and contain something like this:

    {\n \"action\": \"route\",\n \"localPort\": 1234,             \uf0df Change this\n \"remoteName\": \"AmtMachine7\",\n \"remoteNodeId\": \"node//@yw$s5jLUivpzZ49laprt4T0sBaOKImbDAiniothQwccZPukCB696$BvPWAW0Bg2\",\n \"remotePort\": 3389,                \uf0df Change this\n \"username\": \"admin\",\n \"password\": \"\",                \uf0df Note that the password is empty\n \"serverId\": \"D99362D5ED8BAEA8BF9E743B34B242256370C460FD66CB62373C6CFCB204D6D70\n7403E396CF0EF6DC2B3A42F735135FD\",\n \"serverHttpsHash\": \"D9DE9E27A229B5355708A3672FB23237CC994A680B3570D242A91E36B4AE5BC\n96539E59746E2B71EEF3DBDABBF2AE138\",\n \"debugLevel\": 0,\n \"serverUrl\": \"wss://devbox.mesh.meshcentral.com:443/meshrelay.ashx\"\n}\n

    The action file contains almost all the parameters needed to perform the route. It indicates the local and remote ports, the remote computer unique identifier, server location and authentication information and more. You can just put the action file in the same folder as MeshCmd and run MeshCmd, it will automatically pick up the arguments from the meshaction.txt file.

    You may want to change the local and remote port in the action file to suite your needs. Be default, the Microsoft RDP port is the target. If the password is not specified in the meshaction.txt file, you can also specify it as a meshcmd argument.

    C:\\MeshCmd>meshcmd --pass xxxxxxxx \nRedirecting local port 1234 to AmtMachine7:3389. Press ctrl-c to exit. \nNow, the traffic router is ready. You can now RDP to localhost:1234 and login to the remote computer. \n
    "},{"location":"meshcmd/#meshcommander","title":"MeshCommander","text":"

    MeshCommander is a web based Intel AMT management console. MeshCmd has no less then three different versions of MeshCommander built-in, so if you are using Intel AMT, it\u2019s worth a moment to get some knowledge about MeshCommander which is available as a standalone tool along with a full user\u2019s guide at:http://www.meshcommander.com/and also included as part of MeshCentral.

    The three versions included in MeshCmd are:

    • MeshCommander as a local web server.
    • MeshCommander for LMS
    • MeshCommander for firmware.

    In this section, we review the three versions, how they are used and what can be done with them.

    "},{"location":"meshcmd/#meshcommander-local-web-server","title":"MeshCommander local web server","text":"

    You can start MeshCommander on a local web server by typing \u201cmeshcmd meshcommander\u201d. By default, local port 3000 is used, but you can optionally specify the port using \u201c--localport [port]\u201d. Running it with look like this:

    C:\\MeshCmd>meshcmd meshcommander\nMeshCommander running on HTTP port 3000. Ctrl-C to exit. \n

    Once running, use a web browser and go to \u201chttp://localhost:3000\u201d to see the MeshCommander web page. The page will start out without any computers in it and you will have to add some.

    In this mode, the local computers that are added will be stored in the browser\u2019s storage cache. So, clearing the browser\u2019s cache will also clear the list of computers. You can however load and save the list of computers using the \u201cOpen\u2026\u201d and \u201cSave\u2026\u201d buttons. This version of MeshCommander will have some limitations when compared to the full version installed using the Windows .MSI installer. Notably:

    • No certificate management or validation.
    • Mutual-Authenticated TLS is not supported.
    • Kerberos authentication is not supported.
    • IDE-R is not supported.

    This said, features like KVM, Terminal and most Intel AMT configuration options are available which makes this a fairly powerful Intel AMT management console. You can run MeshCommander as a background server on both Windows and Linux. To do this, use the install, uninstall, start and stop commands like this:

    C:\\MeshCmd>meshcmd meshcommander install\nInstalling to \"C:\\Program Files (x86)\\Open Source\\MeshCmd\\MeshCommander.exe\" MeshCommander installed. \n\nC:\\MeshCmd>meshcmd meshcommander start\nMeshCommander starting.\n

    On Linux computers, both systemd and initd are supported. When installing, the MeshCmd executable will be copied to a different installation folder.

    "},{"location":"meshcmd/#lms-meshcommander","title":"LMS & MeshCommander","text":"

    The Layered Management Service (LMS) is a background process that runs in the operating system and provides local OS access to Intel AMT. More specifically, it redirects local TCP ports 16992 and 16993 to Intel AMT thru the MEI driver. One way to check if the LMS service is installed on your computer is to tryhttp://localhost:16992 or https://localhost:16993in a browser. A page should generally show up.

    On Windows, LMS is a Windows service that must be downloaded and installed as part of the OEM drivers for a computer. On Linux, it\u2019s generally never installed and users normally have to download the source code and compile it themselves.

    MeshCmd has its own LMS implementation, so is LMS is not installed it will automatically use its own internal one. In addition, you can run MicroLMS alone by typing the following list while running as root or local administrator:

    C:\\MeshCmd>meshcmd microlms\nMicroLMS started, MeshCommander on HTTP/16994. \n

    This will start MicroLMS and as indicated, start MeshCommander on local port 16994 at the same time. In total, traffic redirection looks like this:

    In addition to providing normal services, MicroLMS that is part of MeshCmd will also start a web server on port 16994 and allow a browser to access LMS MeshCommander, a special version of MeshCommander specially made to run in this situation.

    MeshCommander for LMS will show up even if the computer has Intel AMT un-configured and offer the user the choice to activate Intel AMT and perform configuration actions. In the following screen, we see a computer activated in Client Control Mode. Because it\u2019s in this mode, you can use the web interface to de-activate Intel AMT or login to perform additional configuration.

    MicroLMS along with MeshCommander for LMS is very useful, especially on Linux as it offers a single tool to setup and configuration Intel AMT. If you wish you run MicroLMS without MeshCommander being available on port 16994, run MeshCmd with \u201c--noconsole\":

    C:\\MeshCmd>meshcmd microlms --noconsole\nMicroLMS started. \n

    You can run MicroLMS as a background server on both Windows and Linux. To do this, use the MicroLMS install, uninstall, start and stop commands like this:

    C:\\MeshCmd>meshcmd microlms install\nInstalling to \"C:\\Program Files (x86)\\Open Source\\MeshCmd\\MicroLMS.exe\" \nMicroLMS installed. \n\nC:\\MeshCmd>meshcmd microlms start\nMicroLMS starting. \n

    On Linux computers, both systemd and initd are supported. When installing, the MeshCmd executable will be copied to a different installation folder.

    "},{"location":"meshcmd/#meshcommander-for-firmware","title":"MeshCommander for firmware","text":"

    MeshCmd also includes a surprising version of MeshCommander, the one you can load into the firmware of Intel AMT. Starting with Intel AMT 11.6, you can push into the small ~190k storage space of Intel AMT a replacement to the index.htm page served by the firmware on port 16992 & 16993. In the following picture, the left side is the original Intel AMT web page, the right is the replaced MeshCommander built to go in firmware.

    The firmware version of MeshCommander has support for remote desktop, terminal and all sorts of Intel AMT usages and configuration. Probably the most surprising is that this entire page is between 40k and 100k depending on the version you select. It\u2019s notable that with MeshCommander loaded into Intel AMT firmware, one does not need any other tool except for a browser to perform most Intel AMT maintenance operations.

    C:\\MeshCmd>meshcmd amtloadwebapp --host 192.168.2.144 --pass xxxxxxxx\nUploading MeshCommander...\nVerifying MeshCommander...\nDone.\n

    To get the current state of Intel AMT web storage, type this:

    C:\\MeshCmd>meshcmd amtstoragestate --host 192.168.2.144 --pass xxxxxxxx\nStorage State: {\n  \"information\": {\n    \"version\": 1,\n    \"realms\": 7765759,\n    \"user\": \"admin\"\n  },\n  \"content\": {\n    \"index.htm\": {\n      \"size\": 57246     \uf0df Replacement index.htm with size.\n    }\n  }\n}\n

    Here, a 57k index.htm replacement is present in the Intel AMT flash. You can clear the web storage, revering the web page back to the original like this:

    C:\\MeshCmd>meshcmd amtclearwebapp --host 192.168.2.144 --pass xxxxxxxx\nDone.\n

    When MeshCommander is loaded into Intel AMT, you can access it from a different computer using http://computername:16992 orhttps://computername:16993 You will need to authenticate first before getting access to the web page.

    "},{"location":"meshcmd/#intel-amt-state-activation","title":"Intel AMT state & activation","text":"

    MeshCmd can easily be used to read the local state of the computer and Intel AMT. There are many commands available to do this. The \u201cSMBios\u201d action works on most computers and is used to get basic information about your current system. The output is JSON format.

    C:\\MeshCmd>meshcmd smbios\n{\n  processorInfo: {\n    0: {\n      Processor: \"CPU\"\n      MaxSpeed: \"3800 Mhz\"\n      Cores: 4\n      Threads: 8\n      Populated: 1\n      Status: \"Enabled\"\n      Socket: \"CPU 1\"\n      Manufacturer: \"Intel(R) Corporation\"\n      Version: \"Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz\"\n    }\n  }\n  memoryInfo: {\n    location: \"System Board\"\n    maxCapacityKb: 33554432\n  }\n  systemInfo: {\n    uuid: \"C57B83D9A94E11E18BA1505054503030\"\n    wakeReason: \"Power Switch\"\n  }\n  systemSlots: {\n    uuid: \"C57B83D9A94E11E18BA1505054503030\"\n    wakeReason: \"Power Switch\"\n  }\n  amtInfo: {\n    AMT: true\n    enabled: true\n    storageRedirection: true\n    serialOverLan: true\n    kvm: true\n    TXT: false\n    VMX: true\n    MEBX: \"66.0.0.8\"\n    ManagementEngine: \"8.1.0.57353\"\n  }\n}\n

    If you have Intel AMT on your system, you can use the \u201camtinfo\u201d, \u201camtversions\u201d and \u201camthashes\u201d to get lots of information about Intel AMT current state. These commands use the Intel AMT MEI driver to get this information and require MeshCmd be run as root or administrator:

    C:\\MeshCmd>meshcmd amtinfo\nIntel AMT v8.1.71, activated in client control mode.\nWired Enabled, DHCP, 00:1E:8C:F5:4F:ED, 192.168.2.10.\n\nC:\\MeshCmd>meshcmd amtversions\nMEI Version = MKQ7710H.86A.0072.2017.0519.1347\nFlash = 8.1.71\nNetstack = 8.1.71\nAMTApps = 8.1.71\nAMT = 8.1.71\nSku = 24584\nVendorID = 8086\nBuild Number = 3608\nRecovery Version = 8.1.71\nRecovery Build Num = 3608\nLegacy Mode = False\n\nC:\\MeshCmd>meshcmd amthashes\nVeriSign Class 3 Primary CA-G1, (Default, Active)\n  SHA256: E7685634EFACF69ACE939A6B255B7B4FABEF42935B50A265ACB5CB6027E44E70\nVeriSign Class 3 Primary CA-G3, (Default, Active)\n  SHA256: EB04CF5EB1F39AFA762F2BB120F296CBA520C1B97DB1589565B81CB9A17B7244\n\u2026\n

    In addition to getting Intel AMT state, MeshCmd can activate Intel AMT in client control mode (CCM) and de-activate Intel AMT if it\u2019s in this mode. Doing this is very simple, starting with Intel AMT not being activated, you use the \u201camtccm\u201d and \u201camtdeactivate\u201d actions.

    C:\\MeshCmd>meshcmd amtinfo\nIntel AMT v8.1.71, pre-provisioning state.\n\nC:\\MeshCmd>meshcmd amtccm --pass xxxxxxxx\nSuccess\n\nC:\\MeshCmd>meshcmd amtinfo\nIntel AMT v8.1.71, activated in client control mode.\n\nC:\\MeshCmd>meshcmd amtdeactivate\nSuccess\n

    Note that when using the \u201camtccm\u201d action, you need to provide a password that will be used for authentication into Intel AMT. This password must be strong with at least 8 characters including a lower case, an uppercase, a numeric value and a non-alpha-numeric value.

    "},{"location":"meshcmd/#intel-amt-audit-log","title":"Intel AMT Audit Log","text":"

    One very useful feature of MeshCmd is its ability to fetch the Intel AMT audit log. This can be valuable when doing forensics on a computer or just trying figure out what is being done thru Intel AMT. MeshCmd can pull the audit log on a local computer without any credentials, as long as it\u2019s running as root or administrator, or pull the audit log remotely if usual credentials are provided.

    The Intel AMT audit log will show when a computer\u2019s Intel AMT was activated, when remote desktop sessions where initiated and more. To get the local audit log, just use the \u201cAmtAuditLog\u201d action.

    C:\\MeshCmd>meshcmd amtauditlog\n2004-01-01 19:17:58.000-08:00 - Local: Provisioning Started\n2018-01-26 14:03:16.000-08:00 - Local: Unprovisioning Started\n2018-01-26 14:03:31.000-08:00 - Local: Provisioning Started\n2018-02-01 12:13:14.000-08:00 - admin: KVM Enabled\n2018-02-01 12:16:01.000-08:00 - admin: KVM Session Started\n2018-02-01 12:16:07.000-08:00 - admin: KVM Session Ended\n2018-02-02 10:56:06.000-08:00 - admin: KVM Session Started\n2018-02-02 10:56:08.000-08:00 - admin: KVM Session Ended\n

    To get a remote audit log:

    C:\\MeshCmd>meshcmd amtauditlog --host 192.168.2.144 --user admin --pass xxxxxxxx\n2003-12-31 23:06:58.000-08:00 - $$OsAdmin: Intel(r) ME Time Set\n2017-08-15 06:53:31.000-07:00 - $$OsAdmin: Intel(r) ME Time Set\n2017-10-13 17:55:11.000-07:00 - Local: Provisioning Started\n2017-10-13 17:55:54.000-07:00 - Local: Opt-In Policy Change\n2017-10-15 06:44:38.000-07:00 - admin: KVM Enabled\n

    You can also save the audit log to file using the \u201c--output\" option.

    "},{"location":"meshcmd/#running-intel-amt-script","title":"Running Intel AMT script","text":"

    MeshCmd has a full WSMAN stack built-in and can be used to run \u201c.mescript\u201d file on a target Intel AMT computer. Script file are useful when you want to run a set of actions on one or more Intel AMT computers at once. You can build a .mescript file using the script editor within MeshCommander.

    This script editor allows the user to drag & drop script blocks, set parameters on each block and test the script against a connected Intel AMT computer.

    In addition to building your own scripts, you can download a CIRA setup script from a MeshCentral server. When running this script, Intel AMT will be setup to call back to the server using an encrypted connection. This enables remote management of Intel AMT over the Internet. The CIRA setup script is available in the \u201cAdd CIRA\u201d link for meshes that are Intel AMT only (no agent).

    Once you got the script, run it with MeshCmd like this. You specify the host if it\u2019s not localhost, the password and the script file.

    C:\\MeshCmd>meshcmd amtscript --host 192.168.2.106 --pass xxxxxxxx --script cira_setup.mescript\nScript Started\nPolicies removed successfully\nAdding root certificate...\nManagement Presence Server (MPS) successfully added\nFound matching (primary) mps: Intel(r) AMT:Management Presence Server 0\nSetting policy...\nPolicy added successfully\nRemote Access user interfaces set to: BIOS & OS Enabed\nParsing block parameters\nSetting Environment Detection\nEnvironment Detection set successfully\nScript Completed\n

    In this example, the CIRA setup script was run on a remote computer. After the script is run, the computer got configured and connected back to the server. MeshCentral show the new connection state for this computer.

    "},{"location":"meshcmd/#ide-redirection","title":"IDE Redirection","text":""},{"location":"meshcmd/#video-walkthru_1","title":"Video Walkthru","text":"

    MeshCmd has all the code needed to perform Intel AMT IDE Redirection from the command line. This allows disk images on the administrator\u2019s computer to be remotely mounted to an Intel AMT computer. You need to start with a floppy disk .img file and/or an .iso CDROM file.

    Then use the \u201cAmtIDER\u201d command of MeshCMD to start an IDER session. The help command for AmtIDER looks like this:

    C:\\Temp>meshcmd help amtider\nAmtIDER will mount a local disk images to a remote Intel AMT computer. Example usage:\n\n  meshcmd amtider --host 1.2.3.4 --user admin --pass mypassword --tls --floppy disk.img --cdrom disk.iso\n\nPossible arguments:\n\n  --host [hostname]      The IP address or DNS name of Intel AMT.\n  --user [username]      The Intel AMT login username, admin is default.\n  --pass [password]      The Intel AMT login password.\n  --tls                  Specifies that TLS must be used.\n  --floppy [file]        Specifies .img file to be mounted as a flppy disk.\n  --cdrom [file]         Specifies .img file to be mounted as a CDROM disk.\n  --timeout [seconds]    Optional, disconnect after number of seconds without disk read.\n

    The command is fairly simple. It takes as input a remote host, username/password for Intel AMT login, the disk images and TLS option. One can also specify the timeout option so that MeshCMD will automatically disconnect when no disk read operations are performed for a set number of seconds.

    "},{"location":"meshcmd/#conclusion","title":"Conclusion","text":"

    MeshCmd is a cross-platform command line tools that perform an ever-growing list of actions that are important for remote computer management. MeshCmd works alone or with MeshCentral and MeshCommander to offer a suite of free, opens source and powerful tools that work well together.

    "},{"location":"meshcmd/#license","title":"License","text":"

    MeshCmd and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0

    "},{"location":"meshctrl/","title":"MeshCtrl","text":"

    MeshCmd Guide as .pdf as .odt

    "},{"location":"meshctrl/#video-walkthru","title":"Video Walkthru","text":""},{"location":"meshctrl/#abstract","title":"Abstract","text":"

    This user guide contains all essential information for the user to make full use of MeshCtrl, a command line tool used to perform tasks on the MeshCentral server. This tool is built in NodeJS and can be run on the same computer as the server, or on a different computer. It\u2019s typically used by administrators to perform various management operations on the MeshCentral server.

    "},{"location":"meshctrl/#introduction","title":"Introduction","text":"

    MeshCtrl, also called \u201cMesh Control\u201d, is a command line tool built in NodeJS that comes with MeshCentral and allows command line management operation of the MeshCentral server. It can be run both on the same computer as the MeshCentral server, or on a different computer. This tool allows an administrator to list user accounts, create and remove user accounts, list device groups and much more. All of the operations performed by this tool can also be performed using the MeshCentral website, however it\u2019s sometimes useful to automate and script some management operations from the command line and this tool can do this.

    MeshCtrl will login to the MeshCentral server using a web socket connection, just like the web application does and so, it needs to login to the server using user or administrator credentials. In the next section, we will cover how to login using MeshCtrl, we then follow with the operations that MeshCtrl can perform.

    "},{"location":"meshctrl/#running-meshctrl","title":"Running MeshCtrl","text":"

    MeshCtrl.js is a file that is installed with MeshCentral and is located in the \u201c./node_modules/meshcentral\u201d folder. You can normally run MeshCtrl like this:

    node ./node\\_modules/meshcentral/meshctrl \n

    In this case, when the above command is run without parameters, you should see something like this:

    MeshCtrl performs command line actions on a MeshCentral server.\nInformation at: https://meshcommander.com/meshcentral\nNo action specified, use MeshCtrl like this:\n\n  meshctrl [action] [arguments]\n\nSupported actions:\n  Help [action]             - Get help on an action.\n  ServerInfo                - Show server information.\n  UserInfo                  - Show user information.\n  ListUsers                 - List user accounts.\n  ListDevices               - List devices.\n  ListDeviceGroups          - List device groups.\n  ListUsersOfDeviceGroup    - List the users in a device group.\n  AddUser                   - Create a new user account.\n  RemoveUser                - Delete a user account.\n  AddDeviceGroup            - Create a new device group.\n  RemoveDeviceGroup         - Delete a device group.\n  AddUserToDeviceGroup      - Add a user to a device group.\n  RemoveUserFromDeviceGroup - Remove a user from a device group.\n  SendInviteEmail           - Send an agent install invitation email.\n  Broadcast                 - Display a message to all online users.\n\nSupported login arguments:\n  --url [wss://server]      - Server url, wss://localhost:443 is default.\n  --loginuser [username]    - Login username, admin is default.\n  --loginpass [password]    - Login password.\n  --token [number]          - 2nd factor authentication token.\n  --loginkey [hex]          - Server login key in hex.\n  --loginkeyfile [file]     - File containing server login key in hex.\n  --domain [domainid]       - Domain id, default is empty.\n

    This indicated you have a good version of MeshCentral with support for MeshCtrl. MeshCentral and MeshCtrl will be updated together, so as changes are made to the server, MeshCtrl will also be updated to match.

    The next step is to login to the server with MeshCtrl before we can start performing commands. For this document, we will assume we are connection to the local MeshCentral server on port 443 using TLS. For connecting to a different server, use the --url argument to set a different server.

    "},{"location":"meshctrl/#login","title":"Login","text":"

    There are two ways to login to MeshCentral using MeshCtrl. You can use the normal username/password, or use a login key. In this section we look at both.

    "},{"location":"meshctrl/#login-using-username-password","title":"Login using username & password","text":"

    This is the easiest way to login and start issuing commands is to login using a username and password.

    You do this by using the \"loginuser\" and \"loginpass\" argument like this:

    node ./node_modules/meshcentral/meshctrl serverinfo --loginuser \"admin\" --loginpass \"mypassword\"\n

    In this example we are trying to run the \u201cserverinfo\u201d command using the \u201cadmin\u201d user and \u201cmypassword\u201d as the password. Because \u201cadmin\u201d if the default username for MeshCtrl, you can omit it and just type:

    Authentication token required, use --token [number].\n

    In this case, you need to use the Google Authenticator application or a similar application to enter your time based second factor like this:

    node ./node_modules/meshcentral/meshctrl serverinfo --loginpass \"mypassword\" \u2013token 123456\n

    Once the command is run successfully, you should see a response like this:

    name: devbox.meshcentral.com\nmpsname: devbox.meshcentral.com\nmpsport: 4433\nport: 443\nemailcheck: true\ndomainauth: false\ntlshash: E7A41E1A56D8D716F7D7729B876E48693F2D19C4761F22B1\u2026\nhttps: true\nredirport: 80\n

    This is a typical response for the \u201cserverinfo\u201d command and a good way to test that everything is working as expected.

    "},{"location":"meshctrl/#login-using-the-server-login-key","title":"Login using the server login key","text":"

    This technique needs some setup, but allows MeshCtrl to login as any account without knowing the password of the account, making it very powerful. Care must be taken when doing this and it\u2019s recommended to only be done on the local server itself unless you know when you are doing.

    In this technique, we will get a special encryption key from the server and use this to generate a login token to the server. First, we must activate the login token feature of the server by setting \u201cAllowLoginToken\u201d to \u201ctrue\u201d in the \u201csettings\u201d section of config.json:

    {\n  \"settings\": {\n    \"Port\": 443,\n    \"AllowLoginToken\": true\n  }\n}\n

    Then run the MeshCentral server with the --logintokenkey parameter, you should see something like this:

    node ./node_modules/meshcentral --logintokenkey\nf3bd69a08a2dde4a3423bec8f20d8626b1e6365381f2d9919e7dfe256ace9cbbdea66bed2bdcd00b71eea9d7c727cb6eb37f3148c0c2d240d5ed44c8f3f6795a479c949159dad366571fea4db7f31c24\n

    The hex value presented here is a sensitive secret that allows anyone to create login tokens that can be used for user impersonation. This key is used when MeshCentral does server peering and load balancing for example. You should save this key in a file, for example \u201ckey.txt\u201d, you can do it like this:

    node ./node_modules/meshcentral \u2013logintokenkey > key.txt\n

    Now that you have the server key, you can use MeshCtrl like this:

    node ./node_modules/meshcentral/meshctrl serverinfo \u2013-loginkeyfile key.txt --loginuser admin\n

    Instead of using a password, we use the key file instead. MeshCtrl will generate a time limited cookie and use that to login as \u201cadmin\u201d. Since \u201cadmin\u201d is the default username for MeshCtrl, we can omit this parameter:

    node ./node_modules/meshcentral/meshctrl serverinfo \u2013-loginkeyfile key.txt\n

    Like the username and password example above, the result will be the server information we requested. Something like this:

    name: devbox.meshcentral.com\nmpsname: devbox.meshcentral.com\nmpsport: 4433\nport: 443\nemailcheck: true\ndomainauth: false\ntlshash: E7A41E1A56D8D716F7D7729B876E48693F2D19C4761F22B1\u2026\nhttps: true\nredirport: 80\n
    "},{"location":"meshctrl/#login-into-a-different-domain","title":"Login into a different domain","text":"

    So far, we have seen how to login to a MeshCentral server using a username/password or username/key technique for the default domain. MeshCentral supports many domains at once with each of the domain having its own set of users, groups and administrators.

    To do this, using the username and password, we have to tell MeshCtrl to login using the URL that would be used to access that domain, for example:

    node ./node_modules/meshcentral/meshctrl serverinfo --url wss://server/domainname --loginuser \"admin\" --loginpass \"mypassword\"\n

    or for servers have a DNS name for a domain:

    node ./node_modules/meshcentral/meshctrl serverinfo --url wss://domainname.server --loginuser \"admin\" --loginpass \"mypassword\"\n

    Note the \"--url\" argument is different and so, the server will see MeshCtrl access the server using a different domain.

    Doing a domain login using the login is key file is a bit different. We need to specify both the url to access that domain and specify the domain name explicitly using the --domain argument. For example:

    node ./node_modules/meshcentral/meshctrl serverinfo \u2013-loginkeyfile key.txt --url wss://server/domainname --domain mycustomer\n

    So, we add both the url to access the correct domain and the domain name explicitly. The domain name must be exactly the one that is used in the domain section of the config.json for of MeshCentral. For example, if the MeshCentral config.json file looks like this:

    {\n  \"settings\": {\n    \"Cert\": \"myserver.com\",\n    \"Port\": 443\n  },\n  \"domains\": {\n    \"\": {\n      \"title\": \"Default domain\",\n    },\n    \"customer1\": {\n      \"dns\": \"c1.myserver.com\",\n      \"title\": \"Customer1 domain\",\n    }\n  }\n}\n

    The domain login for MeshCtrl with a key file would look like this:

    node ./node_modules/meshcentral/meshctrl serverinfo \u2013-loginkeyfile key.txt --url wss://c1.myserver.com --domain customer1\n

    In the next section, we start making use of MeshCtrl to do useful things on the server. From now on, we will omit the login arguments, but note that they are required for all commands.

    "},{"location":"meshctrl/#making-use-of-meshctrl","title":"Making use of MeshCtrl","text":"

    We can start doing useful operations with MeshCtrl. The current list of operations are:

    edituser\nlistusers\nlistusersessions\nlistdevicegroups\nlistdevices\nlistusersofdevicegroup\nlistevents\nlogintokens\nserverinfo\nuserinfo\nadduser\nremoveuser\nadddevicegroup\nremovedevicegroup\neditdevicegroup\nbroadcast\nshowevents\naddusertodevicegroup\nremoveuserfromdevicegroup\naddusertodevice\nremoveuserfromdevice\nsendinviteemail\ngenerateinvitelink\nconfig\nmovetodevicegroup\ndeviceinfo\neditdevice\naddusergroup\nlistusergroups\nremoveusergroup\nruncommand\nshell\nupload\ndownload\ndeviceopenurl\ndevicemessage\ndevicetoast\naddtousergroup\nremovefromusergroup\nremoveallusersfromusergroup\ndevicesharing\ndevicepower\nindexagenterrorlog\nagentdownload\nreport\n

    You can get this list by just running MeshCtrl without any argument and can get more information on each action by typing \u201cmeshctrl help [action]\u201d

    Note

    Note that when using Intel AMT only (no agent) you can do wake (on) and power off and reset from the group action. MeshCentral should automatically using Intel AMT to perform these actions when you select \"Wake-up devices\", \"Power off devices\" or \"Reset devices\".

    "},{"location":"meshctrl/#gathering-information","title":"Gathering information","text":"

    The following commands are really easy to use: serverinfo, userinfo, listusers, listdevices, listdevicegroups. They just request information from the server. Note that for these commands, you can optionally use \u201c--json\" to receive the response in JSON format. For example, getting the list of users will look like this:

    node meshctrl.js listusers\nid, name, email\n---------------\n\"admin\", \"admin\", \"username@domain.com\"\n\"joe\", \"joe\", \"joe@domain.com\"\n\"mytestuser\", \"MyTestUser\", \"a@a.com\"\n\"test.user\", \"test.user\", \"test.user@user.com\"\n

    In the --json argument is used, it looks like this:

    node meshctrl.js listusers --json\n  {\n    \"_id\": \"user//admin\",\n    \"name\": \"admin\",\n    \"creation\": 1417814230,\n    \"siteadmin\": 4294967295,\n    \u2026\n

    The JSON version is much long and contain much more information. This format can be useful if you take the output of MeshCtrl and dump it into a file for later parsing. One really useful command is \u201clistdevices\u201d that just displays all the devices in the account grouped by device groups. For example:

    node meshctrl listdevices\n\nDevice group: \"Lab Computers\"\nid, name, icon, conn, pwr, ip\n-----------------------------\np3HOhDapgT@VyO$upGJYxEa$v4YCY76Y2G@hOGmJnbPXjkSHP@AgJ1M6FkqSEUqg, \"raspberrypi\", 5, 1, 1\nyjbMXlQBf09TSIqKlkwrRucm767TcXfNbSinQWXgpdBBY5MEU1gg0kzshwiwFCOp, \"tinkerboard\", 5, 1, 1\nDRvCLkYIgk744tqqMr9Xvy5TK8aXkLoOXUQETnFdFepVQojyFV5gaBi5Gh4f6B6d, \"LattePanda\", 5, 1, 1\nggifepc5wqK7sCVnOIjOZy9i9kaJizalIarz7Qwe5bJ4icpLD69zWYpjAaU@sfY$, \"MeshLabTop\", 1, 5, 1\nECAI7NO893JoN3ntK7@mbniyDq0qriG82wqGKQF4s8SpXs3NdnvuHR76Bzq14Pik, \"MeshLabRight\", 1, 1, 1\nfCLFeHaxQ$T6mgICdVkCdkifiU8LNJdU73YknmxfAb@0jBF2BrhTsEIBwgpoCNx$, \"DevLinux\", 1, 1, 1\nhfbJ7zAgwZK@LQfsZkr1cqTSp6mjjZ3MjGC$v4X8E7HM1cZEnlGBgcorELu1hZWe, \"AmtMachine11\", 1, 1, 1\nYRGm4AQVRR38Ypisuo40KhvBGhDl2pE5YCp4j4eIbLaX3kmH3tmumOUbxb44A@Rh, \"CoffeeTable\", 1, 1, 1\nPpMJiPxtjRjfoEal$9RHdm5s31BaqDSbGc3329s49rzcXcVuTDvm4VO0YllO5XR7, \"DevBox\", 1, 0, 0\ntyR7l2j5@wOjDeRbOQNfjU7xB$ss6VZQPDkFsALPzJ4zbTI4IamV$OdwHeqiXV0K, \"MeshLabLeft\", 1, 5, 1\ni@BNTAHB5NMtDyrHMiCaz3GzYlJUUQn7qZZfh@N6271DWAM3EH6ujRNPc2snGXYX, \"raspberrypi\", 5, 1, 1\n2E$CjXw2Aldh3DGAzSNo5qTSgEhd4OTWcO9KGBi9ja4EOxEUHq8J1135Y2IvxOlX, \"AmtMachine7\", 1, 5, 1\n0Ab3O@4fgHjwVOpC0qaARfURTtKCa@QjxWPDpT5WQ0Wz5s4OvRWAgeoGT9j8k5JF, \"RaspberryPi\", 5, 1, 1\n\nDevice group: \"IntelAMT only\"\nid, name, icon, conn, pwr, ip\n-----------------------------\nLN8qsH634RGjDwATIOd3ICzfKrB@t@ERow8AEyDndyAs3yqwfrfhEaMPABMg53cg, \"AmtMachine7\", 0, 0, 0\nEa3GcF$EoMnDEc9Tbz$Vu9wnmTziqqcOZ0URSdYeuVn4LU9LLMT@91P5s1WLSgVA, \"DevBox\", 0, 0, 0\n

    It\u2019s also possible to list only the display the list of devices for a single group. Just add --id followed by the group identifier. You can find the group identifier using the \u201cDeisplayDeviceGroups\u201d command. For example:

    node meshctrl listdevices --id 7b4b43cdad850135f36ab31124b52e47c167fba055\u2026\n\nid, name, icon, conn, pwr, ip\n-----------------------------\np3HOhDapgT@VyO$upGJYxEa$v4YCY76Y2G@hOGmJnbPXjkSHP@AgJ1M6FkqSEUqg, \"raspberrypi\", 5, 1, 1\nyjbMXlQBf09TSIqKlkwrRucm767TcXfNbSinQWXgpdBBY5MEU1gg0kzshwiwFCOp, \"tinkerboard\", 5, 1, 1\nDRvCLkYIgk744tqqMr9Xvy5TK8aXkLoOXUQETnFdFepVQojyFV5gaBi5Gh4f6B6d, \"LattePanda\", 5, 1, 1\nggifepc5wqK7sCVnOIjOZy9i9kaJizalIarz7Qwe5bJ4icpLD69zWYpjAaU@sfY$, \"MeshLabTop\", 1, 5, 1\nECAI7NO893JoN3ntK7@mbniyDq0qriG82wqGKQF4s8SpXs3NdnvuHR76Bzq14Pik, \"MeshLabRight\", 1, 1, 1\nfCLFeHaxQ$T6mgICdVkCdkifiU8LNJdU73YknmxfAb@0jBF2BrhTsEIBwgpoCNx$, \"DevLinux\", 1, 1, 1\nhfbJ7zAgwZK@LQfsZkr1cqTSp6mjjZ3MjGC$v4X8E7HM1cZEnlGBgcorELu1hZWe, \"AmtMachine11\", 1, 1, 1\nYRGm4AQVRR38Ypisuo40KhvBGhDl2pE5YCp4j4eIbLaX3kmH3tmumOUbxb44A@Rh, \"CoffeeTable\", 1, 1, 1\nPpMJiPxtjRjfoEal$9RHdm5s31BaqDSbGc3329s49rzcXcVuTDvm4VO0YllO5XR7, \"DevBox\", 1, 0, 0\ntyR7l2j5@wOjDeRbOQNfjU7xB$ss6VZQPDkFsALPzJ4zbTI4IamV$OdwHeqiXV0K, \"MeshLabLeft\", 1, 5, 1\ni@BNTAHB5NMtDyrHMiCaz3GzYlJUUQn7qZZfh@N6271DWAM3EH6ujRNPc2snGXYX, \"raspberrypi\", 5, 1, 1\n2E$CjXw2Aldh3DGAzSNo5qTSgEhd4OTWcO9KGBi9ja4EOxEUHq8J1135Y2IvxOlX, \"AmtMachine7\", 1, 5, 1\n0Ab3O@4fgHjwVOpC0qaARfURTtKCa@QjxWPDpT5WQ0Wz5s4OvRWAgeoGT9j8k5JF, \"RaspberryPi\", 5, 1, 1\n

    You can also add \u201c--count\" to just return the number of devices instead of displaying them. An example of this would be:

    node meshctrl listdevices --id 7b4b43cdad850135f36ab31124b52e47c167fba055\u2026 --count\n13\n

    Here we see that in a specific device group, there are 13 devices.

    "},{"location":"meshctrl/#adding-and-removing-accounts","title":"Adding and removing accounts","text":"

    MeshCtrl can be used to add and remove user accounts. This is great is you want to automate MeshCentral in some situations. Normally, an administrator can go to the \u201cMy Users\u201d tab and press \u201cNew Account\u2026\u201d button to create a new account.

    With MeshCtrl the same process can be accomplished using the following command line:

    node ./node_modules/meshcentral/meshctrl adduser --user SampleUser --pass SamplePassword \n

    This will create a basic user account with the specified \u201cSampleUser\u201d username and \u201cSamplePassword\u201d password. In addition to the basic example above, there are plenty of additional arguments that can be used. There is a list:

    --email [email]    - New account email address.\n--emailverified    - New account email is verified.\n--resetpass        - Request password reset on next login.\n--siteadmin        - Create the account as full site administrator.\n--manageusers      - Allow this account to manage server users.\n--fileaccess       - Allow this account to store server files.\n--serverupdate     - Allow this account to update the server.\n--locked           - This account will be locked.\n--nonewgroups      - Account will not be allowed to create device groups\n--notools          - Account not see MeshCMD download links.\n

    Instead of specifying a password, one can specify \u201c--randompass\" to have MeshCtrl generate a random password, this is typically use along with \u201c--resetpass\" and \u201c--email\" to cause the user to perform a password reset the first time a login occurs.

    The permission arguments are used to grant server permissions to the new account. The \u201c-- siteadmin\" argument will grant this account full site administrator rights and all other permission arguments are ignored. The other permission arguments can be combined.

    To remove a user account, use the following command:

    node ./node_modules/meshcentral/meshctrl removeuser --userid SampleId\n

    Note that when creating a new user, the username is passed in. However, when removing a user account, the userid is used. One can get the list of userid\u2019s by using the \u201cListUsers\u201d command.

    MeshCtrl will return \u201cok\u201d if the command is successful and the account will be added in real-time to the \u201cMy Users\u201d tab on the website.

    "},{"location":"meshctrl/#creating-and-removing-device-groups","title":"Creating and removing device groups","text":"

    MeshCtrl can be used to add and remove device groups. When a group is created, it\u2019s added in the account that MeshCtrl is logged into and that account gets full rights over that device group. Typically, a new device group is created using the \u201cAdd Device Group\u201d link in the main website.

    A group has a name, type and optionally a description text. To add a device group using MeshCtrl, we use the \u201cAddDeviceGroup\u201d command like this:

    node ./node_modules/meshcentral/meshctrl adddevicegroup --name \"Sample group name\" --desc \"Sample description\"\n

    Here we specify the name and description of the new device group. This will create a normal device group that requires agents to be installed on each computer. You can also create an agent-less Intel AMT device group by adding the \"--amtonly\" argument. Once done, MeshCtrl will return \u201cok\" and the group will be created in the logged in account in real-time.

    You can delete a group by using the \u201cRemoveDeviceGroup\u201d command like this:

    node ./node_modules/meshcentral/meshctrl removedevicegroup --id 7b4b43cd\u2026dc89fe0e581c\n

    To remove a device group, the group identifier needs to be specified. You can get that identifier using the \"ListDeviceGroups\" command. For device groups, the group identifier is a long hex value.

    "},{"location":"meshctrl/#adding-and-removing-users-from-device-groups","title":"Adding and removing users from device groups","text":"

    Once you created user account and device groups, the next important operation is to connect the two and grant users access to some device groups. In the web interface, this is typically done by clicking on a device group and managing the users in that page.

    To do this, you first have to get the user and group identifiers. You can get these using the \u201clistusers\u201d and \u201clistdevicegroups\u201d commands. Then, you can for example do this:

    node ./node_modules/meshcentral/meshctrl addusertodevicegroup --id 7b4b43cd\u2026dc89fe0e581c --userid bob --fullrights\n

    Typically, the group identifier is a long hex value. The command will add user \u201cbob\u201d to a given group with full rights. You can also add a user to a group with only some rights, the list is as follows:

      --fullrights           - Allow full rights over this device group.\n  --editgroup            - Allow the user to edit group information.\n  --manageusers          - Allow the user to add/remove users.\n  --managedevices        - Allow the user to edit device information.\n  --remotecontrol        - Allow device remote control operations.\n  --agentconsole         - Allow agent console operations.\n  --serverfiles          - Allow access to group server files.\n  --wakedevices          - Allow device wake operation.\n  --notes                - Allow editing of device notes.\n  --desktopviewonly      - Restrict user to view-only remote desktop.\n  --limiteddesktop       - Limit remote desktop keys.\n  --noterminal           - Hide the terminal tab from this user.\n  --nofiles              - Hide the files tab from this user.\n  --noamt                - Hide the Intel AMT tab from this user.\n

    Note that if \u201cfullrights\u201d are granted, all other access right arguments are ignored. If successful, MeshCtrl will display \u201cok\u201d, otherwise it will show an error message.

    To remove a user from a group, use the \u201cremoveuserfromdevicegroup\u201d command. For example:

    node ./node_modules/meshcentral/meshctrl removeuserfromdevicegroup --id 7b4b43cd\u2026dc89fe0e581c --userid bob\n

    The syntax of this command is identical to the \u201caddusertodevicegroup\u201d command, but there are no rights arguments. MeshCtrl will also display \u201cok\u201d if the command got processed correctly.

    "},{"location":"meshctrl/#message-broadcast","title":"Message Broadcast","text":"

    One fun command the MeshCtrl offers is the \u201cbroadcast\u201d command. It sends a short notification message that all connected users will see in real-time. For example, you can do this:

    node ./node\\_modules/meshcentral/meshctrl broadcast --msg \"This is a test\" \n

    All connected users will see this:

    The broadcast command is great for notifying users of upcoming server downtime or other events.

    "},{"location":"meshctrl/#conclusion","title":"Conclusion","text":"

    MeshCtrl is an essential tools in every MeshCentral administrator\u2019s tool box. It allows administrators to automate various MeshCentral management tasks which can be useful for large scale management of many devices and users.

    "},{"location":"meshctrl/#license","title":"License","text":"

    MeshCtrl and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0

    "},{"location":"meshrouter/","title":"MeshCentral Router","text":"

    MeshCentral Router Guide as .pdf as .odt

    "},{"location":"meshrouter/#video-walkthru","title":"Video Walkthru","text":""},{"location":"meshrouter/#abstract","title":"Abstract","text":"

    This document takes a look at MeshCentral Router, a Windows application that performs TCP and UDP port mapping from a local machine to any remote computer thru a MeshCentral servers. This document should allow the user to Internet relay traffic thru NAT routers and firewalls.

    "},{"location":"meshrouter/#introduction","title":"Introduction","text":"

    MeshCentral is a remote management web site that connects users to remote computers over a local network or the internet. In addition to remote management features, MeshCentral is capable of relaying TCP and UDP traffic thru the server and thru mesh agents installed on remote computers. This feature can be useful to, for example, perform a remote desktop session using RDP or route custom traffic that would otherwise be difficult to route.

    "},{"location":"meshrouter/#downloading","title":"Downloading","text":"

    MeshCentral router is a Windows application that comes built-into the MeshCentral server or can be downloaded at: https://meshcentral.com/tools/MeshCentralRouter.exe

    It\u2019s probably best to use the MeshCentral router that comes with your version of the MeshCentral server as the two will likely be most compatible. A given MeshCentral Router version may not work with any MeshCentral server versions. On MeshCentral, you can download MeshCentral Router with this link:

    The router link will only show up on Windows browsers.

    "},{"location":"meshrouter/#login","title":"Login","text":"

    MeshCentral router will need to login to your MeshCentral server just like any browser. You can do this by entering the server name and account username and password. Depending on your server and account situation, you may see some or all of the following screens.

    If the second factor authentication is required, MeshCentral Router does not support hardware keys (WebAuthn), but does support the YubiKey\u2122 OTP.

    "},{"location":"meshrouter/#creating-port-maps","title":"Creating Port Maps","text":"

    Once logged in, you can start adding port maps using the \u201cAdd Map\u2026\u201d and \u201cAdd Relay Map\u2026\u201d buttons on the bottom right. You can then create a map and open the associated application using the \u201cOpen\u2026\u201d button and remote to port map using the \u201cRemove\u201d button.

    There are two different types of ports mappings. A normal port map and a relay port map. A normal port map will route packets to the selected destination computer that is running the mesh agent as shown here.

    A relay port map will route traffic thru the server and thru the remote agent to a target IP address on the remote agent\u2019s network as shown here.

    Note that all traffic is encrypted using TLS from MeshCentral Router to the MeshCentral server and from the server to the MeshAgent. The server and the agent do have access the traffic so it\u2019s recommended to use port mappings to tunnel data that is also encrypted for that end-to-end encryption is assured.

    "},{"location":"meshrouter/#custom-apps","title":"Custom Apps","text":"

    Starting v1.0.21

    "},{"location":"meshrouter/#command-line-arguments","title":"Command Line Arguments","text":"

    MeshCentral router can be run with command line arguments to make it quicker and easier to use. The arguments range from debugging to being able to quickly login and setting up port maps.

    -debug\n

    Causes MeshCentral Router to generate a \u201cdebug.log\u201d dump file that can be useful for debugging.

    -host:<hostname>\n-user:<username>\n-pass:<password>\n-ignorecert\n

    This set of command line arguments make logging into the MeshCentral server easier. Note that specifying the password using a command line argument may not be secure as the command shell can record the password in the command history. The \u201cignorecert\u201d argument is not recommended as it\u2019s going to cause MeshCentral Router to ignore untrusted server certificates. This should only be used for debugging.

    -map:<protocol>:<localport>:<computername>:<app>:<remoteport>\n

    The \u201cmap\u201d argument will automatically create a network map once MeshCentral Router is logged In. The protocol must be \u201cTCP\u201d or \u201cUDP, the local port can be 0 for any. The computer name is the server-side name of the computer, if many computers have the same one, one of them will be selected. The app can be empty for a custom application, or can be \u201cHTTP\u201d, \u201cHTTPS\u201d, \u201cRDP\u201d, \u201cPuTTY\u201d or \u201cWinSCP\u201d. For the UDP protocol, no apps are currently supported so it should be left blank. For example you can use:

    -map:TCP:0:\"MyComputer\":HTTP:80\n-map:UDP:1235:\"MyComputer\"::1234\n

    The first example will map a random local port to port 80 of \u201cMyComputer\u201d and is expected for use with HTTP traffic. The second example maps local UDP port 1235 to port 1234 on \u201cMyComputer\u201d. It\u2019s best for the computer name to be in quotes.

    In addition to port mapping, you can also setup relay maps where a remote computer is used as a traffic relay like this:

    -relaymap:<protocol>:<localport>:<computername>\n :<app>:<remoteip>:<remoteport>\n

    This will relays a local port to thru the server and thru a remote agent to a target IP address and port. For example:

    -relaymap:TCP:555:\"MyComputer\":HTTP:192.168.1.1:80\n

    This will relay local port 555 to a 192.168.1.1:80 for HTTP traffic. A typical use of this is to be able to remotely configure a home router from anywhere on the Internet.

    -all\n

    The \u201call\u201d switch will bind local ports to all network interfaces so that other computers on the network can use the port maps provided by MeshCentral Router. By default, local ports will be bound to the loopback interface so that only local application can use the port mappings.

    -tray\n

    The \u201ctray\u201d switch will place MeshCentral Router on the Windows system tray instead of the normal application bar.

    "},{"location":"meshrouter/#meshcentral-router-with-saml-or-oauth","title":"MeshCentral Router with SAML or OAuth","text":"

    You can't use the MeshCentral Router (MCR) login dialog box to login to a server that uses SAML or OAuth. However, you can still use MCR

    1. Download MCR and run it.
    2. In the first dialog box, there will be an \"Install...\" button to setup MeshCentral router with the \"mcrouter://\" protocol in your system registry.
    3. Once done, close MCR.
    4. Now log into the web UI of your MeshCentral server. Go in the bottom of the \"My Devices\" tab, hit the \"Router\" link and hit \"Launch MeshCentral Router\".
    5. This will launch the router and connect directly to your server using a login cookie.

    Note

    The only drawback is you will have to manually load mappings saved in an .mcrouter file. When not using SAML, you can click on the .mcrouter file to load the mappings and MCR at the same time.

    "},{"location":"meshrouter/#conclusion","title":"Conclusion","text":"

    MeshCentral Router is a free, open source tool for routing TCP and UDP traffic over the internet. It\u2019s a powerful as it allows for any traffic to go thru NAT routers and firewalls while being encrypted using TLS.

    "},{"location":"meshrouter/#license","title":"License","text":"

    MeshCentral, MeshCentral Router and this document are all opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0.

    "},{"location":"messaging/","title":"Messaging","text":""},{"location":"messaging/#table-of-contents","title":"Table of Contents","text":"

    Introduction Telegram Setup Discord Setup XMPP Setup CallMeBot Setup Pushover Setup ntfy Setup Zulip Setup User Setup Administrator Management Two-Factor Authentication

    "},{"location":"messaging/#introduction","title":"Introduction","text":"

    MeshCentral supports messaging platforms so that users can register a messaging account with MeshCentral and receive notifications. This is useful since messages are sent to an application the user is confirtable with and many messaging platforms have clients on mobile phones, desktop and more so that the notification can show up where the user is.

    "},{"location":"messaging/#telegram-setup","title":"Telegram Setup","text":"

    For Telegram integration, you will need to provide MeshCentral with the necessary login information so that MeshCentral can authenticate and connect to the Telegram servers and start sending notifications. For Telegram, both user and bot login is supported with bot login being the more typical way to go. The configuration in the config.json for a bot login looks like this:

    {\n  \"settings\": {\n    \"Cert\": \"devbox.mesh.meshcentral.com\",\n  },\n  \"domains\": {\n    \"\": {\n      \"title\": \"My Server\"\n    }\n  },\n  \"messaging\": {\n    \"telegram\": {\n      \"apiid\": 0,\n      \"apihash\": \"00000000000000000000000\",\n      \"bottoken\": \"00000000:aaaaaaaaaaaaaaaaaaaaaaaa\"\n    }\n  }\n}\n

    Note the \"messaging\" section in the config.json. For Telegram user login, it looks like this:

    {\n  \"messaging\": {\n    \"telegram\": {\n      \"apiid\": 0,\n      \"apihash\": \"00000000000000000000000\",\n      \"session\": \"aaaaaaaaaaaaaaaaaaaaaaa\"\n    }\n  }\n}\n

    User login makes use of \"session\", while bot login uses \"bottoken\". One way to get started with the setup is to run node node_modules/meshcentral --setuptelegram and follow the instructions.

    In the first step, you will get the apiid and apihash values. In the second step you get the bottoken or enter your phone number and code to get the session value. Once done, when running the server manually from the command line, the server should indicate that it can connect to Telegram like this:

    MeshCentral HTTP redirection server running on port 80.\nMeshCentral v1.0.87, Hybrid (LAN + WAN) mode.\nMeshCentral Intel(R) AMT server running on central.mesh.meshcentral.com:4433.\nMeshCentral HTTPS server running on central.mesh.meshcentral.com:443.\nMeshCentral HTTPS relay server running on relay1.mesh.meshcentral.com:443.\nMeshCentral Telegram client is bot connected.\n

    Note the last line, indicating it's connected as a bot. If you wish to use Telegram with a proxy, here are the possible Telegram settings. You can use the proxy settings for both user or bot login modes.

    {\n  \"messaging\": {\n    \"telegram\": {\n      \"apiid\": 0,\n      \"apihash\": \"00000000000000000000000\",\n      \"session\": \"aaaaaaaaaaaaaaaaaaaaaaa\",\n      \"useWSS\": false,                                 // Important. Most proxies cannot use SSL.\n      \"proxy\": {\n        \"ip\": \"123.123.123.123\",                       // Proxy host (IP or hostname)\n        \"port\": 123,                                   // Proxy port\n        \"MTProxy\": false,                              // Whether it's an MTProxy or a normal Socks one\n        \"secret\": \"00000000000000000000000000000000\",  // If used MTProxy then you need to provide a secret (or zeros).\n        \"socksType\": 5,                                // If used Socks you can choose 4 or 5.\n        \"timeout\": 2                                   // Timeout (in seconds) for connection,\n      }\n    }\n  }\n}\n
    "},{"location":"messaging/#discord-setup","title":"Discord Setup","text":"

    For Discord integration, you need to provide MeshCentral with a bot application token so that MeshCentral can login and send notifications to users. The Discord bot will need to be joined to one or more Discord servers and users will need to join at at least one Discord server that is in common with the bot to receive notifications.

    There are many tutorials online on how to create a Discord bot and get the login token. For example follow the two first sections of this tutorial. The \"How to Create a Discord Bot Account\" section will show how to create a bot and get the token, the \"How to Invite Your Bot to Join a Server\" section shows how to join the bot to a Discord server.

    Note that Privleged Gateway Intents permissions is needed for the bot on Discord. If not set, MeshCentral will show an error when trying to connect to Discord.

    Discord integration requires that MeshCentral be run on NodeJS v17 or higher. Once you have the Discord bot login token, the config.json Discord configuration looks like this:

    {\n  \"messaging\": {\n    \"discord\": {\n      \"serverurl\": \"https://discord.gg/xxxxxxxxx\",\n      \"token\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n    }\n  }\n}\n

    Once users will need to join the same Discord server as the bot, the optional \"serverurl\" can be used to give the users a URL link to join the server, this can be a server invitation link or some other URL with instructions.

    "},{"location":"messaging/#xmpp-setup","title":"XMPP Setup","text":"

    For XMPP integration, you need to provide MeshCentral with a XMPP server, username and password so that MeshCentral can login and send notifications to users. You can get a XMPP account to any number of servers or start up your own XMPP server.

    {\n  \"messaging\": {\n    \"xmpp\": {\n      \"service\": \"xmppserver.com\",\n      \"credentials\": {\n        \"username\": \"username\",\n        \"password\": \"password\"\n      }\n    }\n  }\n}\n

    An easy way to get setup with XMPP is to create a free account with chatterboxtown.us and then, setup MeshCentral with the service value set to \"chatterboxtown.us\" along with the username and password of you account. This can be done in minutes. Once setup, users will be able to setup and verify XMLL accounts and use this for notifications and 2FA verification.

    "},{"location":"messaging/#callmebot-setup","title":"CallMeBot Setup","text":"

    CallMeBot is a free system that allows users to receive notifications on Signal Messenger, Whatsapp and Facebook Messenger. Enabling this feature is very simple, just enable it like this:

    {\n  \"messaging\": {\n    \"callmebot\": true\n  }\n}\n

    Once enabled, users see the new \"CallMeBot\" option when trying to enable messaging in MeshCentral. They will need to follow specific instructions to enable CallMeBot to send notifications to their messaging application.

    • Signal Messenger
    • Whatsapp
    • Facebook Messenger
    • Telegram

    Once the user has enabled their account, they can cut & paste the CallMeBot URI into MeshCentral to validate their account.

    "},{"location":"messaging/#pushover-setup","title":"Pushover Setup","text":"

    Pushover is another notification service that makes it's own mobile application. To get started, download the Pushover application and create an account then go to the https://pushover.net/ web site and setup a new application like this:

    You can setup an application with a name and icon, then, once you get a application token you can add it to the config.json like this:

    {\n  \"messaging\": {\n    \"pushover\": {\n      \"token\": \"xxxxxxxxxxxxxxxxxxxxx\"\n    }\n  }\n}\n

    Once setup, the server will offer users the \"Pushover\" option when setting up messaging. Users will need to copy the Pushover user key into the dialog box to verify notifications are correct.

    "},{"location":"messaging/#ntfy-setup","title":"ntfy setup","text":"

    ntfy is a completely free notification service. You can enable the ntfy integration with the following config.json section:

    {\n  \"messaging\": {\n    \"ntfy\": true\n  }\n}\n
    No setup is reqired to enable this. When using ntfy, make sure you use a suffisently random topic name so that others can't guess the name and subscribe to it to receive your messages.

    For self-hosting your own ntfy server with ACL support

    You can set host to the DNS name of your server, userurl to the url to provide to users to setup access to your server and authorization to the Basic base64 User+Pass authenttication for your server

    {\n  \"messaging\": {\n    \"ntfy\": {\n      \"host\": \"myntfyserver.com\",\n      \"userurl\": \"https://myntfyserver.com/userhelp\",\n      \"authorization\": \"Basic cGhpbDpteXBhc3M=\"\n    }\n  }\n}\n

    "},{"location":"messaging/#zulip-setup","title":"Zulip setup","text":"

    You can enable the MeshCentral Zulip integration with the following config.json section:

    {\n  \"messaging\": {\n    \"zulip\": {\n      \"site\": \"https://api.zulip.com\",\n      \"email\": \"your-bot@zulip.com\",\n      \"api_key\": \"your_32_character_api_key\"\n    }\n  }\n}\n
    "},{"location":"messaging/#slack-setup","title":"Slack setup","text":"

    Slack integration is achieved by the use of Incoming Webhooks. You can get started by following the Slack guide here and getting your URL

    Once you have your incoming webhooks url, You can enable the Slack integration with the following config.json section

    {\n  \"messaging\": {\n    \"slack\": true\n  }\n}\n
    "},{"location":"messaging/#user-setup","title":"User Setup","text":"

    Once one or more messaging systems are setup with MeshCentral, users will be able to register their handle and verify that they own that account by typing in a 6 digit code.

    This verification is necessary so that MeshCentral does not send notifications to incorrect messaging accounts.

    "},{"location":"messaging/#administrator-management","title":"Administrator Management","text":"

    When users setup a messaging account, a messaging bubble will show up next to their name in the \"My Users\" tab. You can also click on a user to see and edit it's messaging handle and message them. Currently MeshCentral can only send messages, no receive.

    "},{"location":"messaging/#two-factor-authentication","title":"Two-Factor Authentication","text":"

    By default, messaging is used as a second factor for login when a user enabled a messaging account. Users will need to messaging icon on the login screen and can opt to receive a 6 digit code to login.

    As an administrator you can turn off use of messaging for 2FA using the following settings in the config.json:

    {\n  \"settings\": {\n    \"Cert\": \"devbox.mesh.meshcentral.com\",\n  },\n  \"domains\": {\n    \"\": {\n      \"title\": \"My Server\",\n      \"passwordRequirements\": {\n        \"msg2factor\": false\n      }\n    }\n  }\n}\n

    Notice the msg2factor is set to false. In this case, messaging can still be used for user notifications, but will not be offered as a 2FA option.

    For administrators, login reports will show if \"Messaging\" was used as a second factor for a user login. You can see this in this report:

    "},{"location":"other/adfs_sso_guide/","title":"ADFS SSO Guide","text":""},{"location":"other/adfs_sso_guide/#assumptions","title":"Assumptions","text":"

    The following guide was built under the assumptions that:

    1. ADFS 4.0 running on Server 2016 using Active Directory
    2. Main ADFS setup already completed / working. SSL certs installed and port forwarded as expected.

    The guide was built to deal specifically with adding mesh as a Relying Party. I\u2019m far from an ADFS expert and some configurations may not be needed. Most of this was built by reading the code and taking guesses as to the needed values.

    "},{"location":"other/adfs_sso_guide/#guide","title":"Guide","text":"

    As with anything SSO, you need 2 pieces \u2013 the IDP setup (in this case ADFS) and the SP setup (in this case Mesh).

    "},{"location":"other/adfs_sso_guide/#mesh-setup","title":"Mesh Setup","text":"

    Add the following to your mesh config file in the domains part:

    Callback URL: Should be the FQDN for your Mesh Server, ending with /auth-saml-callback

    Entity ID: This is how ADFS IDs which party the request goes to. You can set this to whatever you want, but you will need this value later on when working in ADFS.

    IDP URL: This is the URL to ADFS. Ends with /adfs/ls unless you did something very weird in ADFS.

    Cert: You will need to export the token signing cert from ADFS, then convert it to PEM format. This cert can be found in ADFS -> Service -> Certificates. You can use this openssl command to convert it from CRT to PEM format:

    openssl x509 -in mycert.crt -out mycert.pem -outform PEM\n

    Save the config and restart the mesh server.

    "},{"location":"other/adfs_sso_guide/#windows-server-configuration","title":"Windows Server Configuration","text":"

    ADFS setup (in pictures):

    Relying Party Trust -> New Relying Party Trust

    Edit the new \u201cRelying Party Trust\u201d Properties:

    Then it\u2019s time to add Claims\u2026

    For the outgoing claim type, where it says firstname, lastname, and email, manually type it in as shown. All other fields should be selected from the dropdown.

    Add another rule:

    For all fields, select from the dropdowns

    "},{"location":"other/meshcentral_satellite/","title":"MeshCentral Satellite","text":"

    Coming Soon

    "},{"location":"other/meshcentral_satellite/#abstract","title":"Abstract","text":"

    MeshCentral Satellite is a Windows application that acts as a relay between a Windows Active Directory Domain and a MeshCentral Server. Once setup, the MeshCentral server can request that MeshCentral Satellite create an 802.1x computer profile for Intel AMT in the domain, or ask a certificate authority to issue or revoke a certificate.

    "},{"location":"other/meshcentral_satellite/#introduction","title":"Introduction","text":"

    A MeshCentral server can run in the cloud on a Linux server, but it may occasionally need to interact with a domain controller to perform some operations. MeshCentral Satellite is built to perform this function. MeshCentral Satellite is a Windows application built in C# and must run on a computer that is part of a domain and must run with sufficient rights to perform LDAP object addition and removal. If a certificate authority (CA) needs to be used, MeshCentral Satellite needs to have sufficient rights to ask the CA issue or revoke certificates.

    MeshCentral Satellite should run on a computer that is always on. Once running, it will attempt to connect to the MeshCentral server and keep the connection open awaiting requests.

    Currently, MeshCentral Satellite can perform four operations on behalf of the MeshCentral server:

    • Create an Intel AMT domain computer.
    • Remove an Intel AMT domain computer.
    • Issue a certificate for Intel AMT.
    • Revoke an Intel AMT certificate.

    MeshCentral Satellite can run both as a standalone application which is practical to get started and it can be setup as a background Windows Service for long term operations. To get started, we will run it as a standalone application and start working on getting it setup.

    "},{"location":"other/meshcentral_satellite/#installation-and-configuration","title":"Installation and Configuration","text":"

    Start by creating an empty folder on a computer that is part of the domain you need to interact with. For example, create a \u201cc:\\MeshCentralSatellite\u201d then copy \u201cMeshCentralSatellite.exe\u201d into that folder. You can find that executable in the \u201cnode_modules/meshcentral/agents\u201d folder of your server. If it\u2019s not present, update your server to the latest version and look again.

    Once started you should see something this:

    Go in the file menu and select \u201cSettings\u2026\u201d, this is where all of the MeshCentral Satellite setting are. It includes sections to connect to your MeshCentral server. Security groups you want device to join and Certificate settings.

    First, enter your MeshCentral server hostname and username and password. MeshCentral Satellite can\u2019t use two-factor authentication (2FA) so you need to create a user account in MeshCentral for Satellite or you can create a login token in the \u201cMy Account\u201d section of MeshCentral with an unlimited expire time and cut & paste the username and password given into MeshCentral Satellite.

    If your MeshCentral server is a test server that does not have a real TLS certificate, you can opt to ignore the TLS certificate on connection but this is not secure and so, not recommended.

    The \u201cDevice Name\u201d has two options, \u201cOperating System Name\u201d or \u201cNode Identifier\u201d. If you opt for \u201cOperating System Name\u201d, Intel AMT devices will show as, for example, \u201ciME-ComputerName\u201d in the list of domain computers. If \u201cnode identifier\u201d is used, Intel AMT devices will look like \u201ciME-xxxxxxxxxxx\u201d where xxx is the start of the MeshCentral node identifier for this device.

    Using the node identifier is more secure as it can\u2019t easily be replicate by any other device. The operating system name would be impersonated by another device causing various security issues.

    The security groups section will list any security groups created until the \u201cComputers\u201d section of the domain controller. Checking one or more of these security groups will automatically going new Intel AMT devices to these groups.

    Lastly, we have the certificate authority and certificate settings. If a certificate authority needs to used, enter the name of the CA which is in the format \u201c\\\u201d you can then hit the check box next to the name and select the certificate template to use.

    For certificate configuration, you can leave it as-is with \u201cSAM Account Name\u201d and the common name and all alternative names selected.

    Once done, you can hit ok. The settings will be saved in a file called \u201cconfig.txt\u201d in plain text in the same folder as \u201cMeshCentralSatellite.exe\u201d. Make sure not to grant access to this file to anyone not authorized to do so as it will have the MeshCentral login username and password.

    Once done, select \u201cLocal Connect\u201d the \u201cFiles\u201d menu to connect to the MeshCentral server.

    MeshCentral Satellite should be connected and ready to receive commands from the server.

    "},{"location":"other/meshcentral_satellite/#checking-the-connection","title":"Checking the connection","text":"

    In order to make sure the server correctly recognizes the MeshCentral Satellite connection, you can go to the server console and type \u201cusersession\u201d. The Satellite session should be marked.

    Currently, you should only have a single satellite session per user. In the future, multiple sessions could be supported for redundancy.

    "},{"location":"other/meshcentral_satellite/#configuring-intel-amt-8021x","title":"Configuring Intel\u00ae AMT 802.1x","text":"

    To start using MeshCentral Satellite, you can configure Intel AMT with an 802.1x profile, this is done in the domain section of the config.json. Here is an example of an 802.1x EAP-TLS profile that will require that Intel AMT be issued a certificate:

          \"AmtManager\": {\n        \"802.1x\": {\n          \"AuthenticationProtocol\": \"EAP-TLS\",\n          \"SatelliteCredentials\": \"admin\"\n        },\n        \"WifiProfiles\": [\n          {\n            \"ssid\": \"Network-8021x\",\n            \"authentication\": \"wpa2-802.1x\",\n            \"encryption\": \"ccmp-aes\"\n          }\n        ]\n      }\n

    In this following example, MSCHAPv2 is used and so, MeshCentral Satellite will need to generate a random password, save it in the active directory and send the password back to MeshCentral for Intel AMT configuration:

          \"AmtManager\": {\n        \"802.1x\": {\n          \"AuthenticationProtocol\": \"PEAPv0/EAP-MSCHAPv2\",\n          \"SatelliteCredentials\": \"admin\"\n        },\n        \"WifiProfiles\": [\n          {\n            \"ssid\": \"Network-8021x\",\n            \"authentication\": \"wpa2-802.1x\",\n            \"encryption\": \"ccmp-aes\"\n          }\n        ]\n      }\n

    The second example does not require that a certificate authority be setup, the first example does. In both cases, the WIFI profile is set to \u201cwpa2-802.1x\u201d and so, the 802.1x profile will be setup for both the Intel AMT wired interface and the specified WIFI profile for wireless.

    Note that is both examples, \u201cSatelliteCredentials\u201d indicates the account name that MeshCentral Satellite will be connected on. In our case, we used the \u201cadmin\u201d account that matches the account configuration we used in sections 3 and 4.

    Make these changes to the config.json and restart the MeshCentral server. Once done, any device groups that are set to configure Intel AMT will generate operations for MeshCentral Satellite.

    "},{"location":"other/meshcentral_satellite/#computer-and-certificate-operations","title":"Computer and certificate operations","text":"

    Once MeshCentral and MeshCentral Satellite are setup, make sure a device group has an active Intel AMT policy. In the example below, we have a device group with an Admin Control Mode (ACM) activation policy.

    Computers connecting to this device group will automatically be setup with the new 802.1x and WIFI profile, but you can go in an agent console and type \u201camtconfig\u201d to force the check of the Intel AMT configuration. In our case, it looks like this:

    MeshCentral is adding a new WIFI profile, setting up 802.1x and issuing a new Intel AMT certificate from the domain CA. This was all done in a few seconds. On the MeshCentral Satellite side, we see this:

    The MeshCentral Satellite received an 802.1x EAP-TLS request. It asked Intel AMT to generate a RSA key pair, to sign a certificate request, forwarded the request to the domain CA for signature and finally returned the final certificate to Intel AMT. The computer account in the domain was also updated and looks like this:

    The new Intel AMT device was added to the domain along with the Intel AMT version and node identifier in the description. If \u201cNode Identifier\u201d was selected as the computer name in MeshCentral Satellite settings, the friendly name would be in the description and the node identifier would be used as the device name.

    Finally, it\u2019s worth taking a look at how Intel AMT was configured before and after this operation. Before setting up the 802.1x profile, Intel AMT looked like this:

    Note that there is no 802.1x profiles or WIFI profiles. After the new configuration, MeshCommander shows Intel AMT looking like this:

    There are now two new certificates in the \u201cSecurity\u201d tab. One if the root of the domain CA, the other is the certificate assigned to the Intel AMT device by the CA. You also see the WIFI 802.1x profile. In this example, the device did not have a wired network interface, but if it did, 802.1x would also be setup for the wired interface.

    "},{"location":"other/meshcentral_satellite/#running-as-a-background-service","title":"Running as a Background Service","text":"

    MeshCentral Satellite can be run as a background service. This is useful when running for lang periods on a domain server. You can use the \u201cService\u201d menu in MeshCentral Satellite to install, start, stop and uninstall the Windows service. Make sure to come and run \u201cMeshCentralSatellite.exe\u201d from the correct location you want to install the Windows Service from.

    Since MeshCentral Satellite need to have domain rights to add and remove computer objects from the active directory and to have certificate authority (CA) rights, you may want to install the service, go in the service manager and change the \u201cLog On\u201d account to one with the proper rights.

    Once set, you can start the service from within MeshCentral Satellite. Once the service is started, there is a communication channel that will be create with the local application so you can still monitor what the service is doing.

    The lines starting with \u201cService:\u201c are coming from the background service. At this point, you can close the local application and the service will keep running in the background.

    "},{"location":"other/meshcentral_satellite/#license","title":"License","text":"

    MeshCentral, MeshCentral Satellite and this document are both opens source and licensed using Apache 2.0, the full license can be found at https://www.apache.org/licenses/LICENSE-2.0.

    "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..9a6ae17e --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,111 @@ + + + + https://ylianst.github.io/MeshCentral/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/design/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/how-to-contribute/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/install/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/install/install2/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/intelamt/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/SSLnletsencrypt/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/agents/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/assistant/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/codesigning/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/config/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/customization/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/debugging/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/devicetabs/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/faq/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/openidConnectStrategy/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/plugins/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/security/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/tipsntricks/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcentral/tokens/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshcmd/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshctrl/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/meshrouter/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/messaging/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/other/adfs_sso_guide/ + 2024-10-15 + + + https://ylianst.github.io/MeshCentral/other/meshcentral_satellite/ + 2024-10-15 + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 00000000..618ba874 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/stylesheets/extra.css b/stylesheets/extra.css new file mode 100644 index 00000000..721e0b33 --- /dev/null +++ b/stylesheets/extra.css @@ -0,0 +1,99 @@ +/* Maximum space for text block */ +.md-grid { + max-width: 95%; /* or 100%, if you want to stretch to full-width */ + } + +.md-header { + background-color: #0b3e81 !important; + color: white !important; +} +.md-search__input { + background-color: white !important; +} +.md-search__icon[for=__search]{ + color: initial; +} +.md-container { + background-color: #0b3e81; +} +.md-footer-meta__inner { + background-color: #0b3e81; +} +.md-grid { +} +.md-nav__item .md-nav__link--active { + color: white; +} +.md-nav__link { + color: white; +} +.md-nav__link[data-md-state=blur] { + color: white; +} +.md-tabs { + background-color: #0b3e81; + color: white; +} +.md-tabs__list { +} +.md-typeset a { + color: whitesmoke; + text-decoration: underline; + text-shadow: 4px 4px 8px black; +} +.md-typeset h1 { + color: white; + text-shadow: 4px 4px 8px black; +} +.md-typeset h2 { + color: white; + text-shadow: 4px 4px 8px black; +} +.md-typeset h3 { + color: white; + text-shadow: 4px 4px 8px black; +} +.md-typeset h4 { + color: white; + text-shadow: 4px 4px 8px black; +} +.md-typeset h5 { + color: white; + text-shadow: 4px 4px 8px black; +} +.md-typeset table { + color: black; +} +.md-main { + color: white !important; + background-color: #113962; + background: linear-gradient(to bottom, #104893 0%,#113962 100%); +} +a:link { + color: #c8c8c8; +} +.html { + background-color: #0b3e81; +} +.dlspan { + background-color:gray; + color:black; + font-size:16px; + padding:4px; + border-radius:4px; + box-shadow: 2px 2px 4px black; +} +.md-typeset .tabbed-set { + border-left-width: 0.2rem; + border-left-color: rgb(244, 244, 244); + border-left-style: solid; + border-radius: 4px; + background-color: rgba(68,138,255,.1); + border-color: rgb(244, 244, 244); + padding: 5px; +} +@media only screen and (max-width: 76.1875em) { + .md-nav__link { + color: black; + } + }

Abstract