RustDesk

Au fil des ans TeamViewer s’est imposé dans l'IT. Au départ pseudo gratuit ce produit offre maintenant de plus en plus de fonctionnalités (souvent inutiles) et son cout augmente d’années en années. On pourrait imaginer le remplacer par AnyDesk, mais hélas leur politique est quasiment identique et ce serait reculer pour mieux sauter.

Pendant le confinement certains en ont eu assez de se faire “raquetter”. Si le mot vous parait fort, constatez simplement les augmentations successives dont la dernière de 20% que rien ne justifie à mes yeux, l'éditeur se justifiant quant à lui par l'ajout de fonctionnalités qui me sont inutiles. Entendons nous bien, tout a un prix et tout travail ou service mérite une rémunération, mais tout doit rester dans la nuance, même quand on se retrouve dans une position de domination, ce qui est d'ailleurs le cas pour d'autres, comme Microsoft et ses dernières augmentations au niveau 365 ou Azure...

Bref, c'est donc ainsi qu'une petite équipe de développeurs a créé RustDesk. RustDesk offre les mêmes fonctionnalités de base dans le cadre de l’open source. Je teste Rustdesk depuis près d’un an et j’estime que l’heure est venue de franchir le pas.

RustDesk est disponible sous différentes distributions Linux, MacOS, Windows, Android et IOS.

Il existe plusieurs façons d’utiliser RustDesk :

Version isolée sans serveur

La plus simple et totalement gratuite, mais qui présente quelques inconvénients :

  • Pas de chiffrement, cela veut dire que toutes les infos circulent en clair (pas sécure pour les clients, mauvaise pratique).
  • Lenteurs possibles car ça utilise les serveurs publics, ce qui reste limité.
  • Pas de carnet d’adresse synchronisé entre les devices clients. Et là ça bloque car comme beaucoup de monde j'utilise un PC fixe au bureau et un laptop quand je me déplace, sur lequel je souhaite retrouver mon environnement de travail. Ils auraient pu envisager une synchronisation de type "drive", mais cela aurait posé des questions de sécurité car en plus de synchroniser les identifiants il faut stocker des mots de passe.

Version gratuite avec serveur

Toujours gratuit (en dehors du cout d'un serveur VPS (qui peut être mutualisé)), et on résous la question du chiffrement :

  • Le serveur sert de relais (amélioration de la vitesse) et de serveur de chiffrement afin de sécuriser les liens.

Version payante avec serveur privé

La totale qui va couter quelques euros (10 € / mois pour deux utilisateurs, ou 20 € /mois pour 20 utilisateurs + le serveur), mais à mon sens indispensable dans un cadre professionnel

  • Le serveur offre une console qui permet de lister les clients installés et éventuellement les partager
    • Seul l’admin peut activer les partages à ce stade. Partager fait remonter les ID pour un autre utilisateur, mais le mot de passe associé au device reste nécessaire.
  • Avoir des logs, qui se connecte à quoi (dispo pour chaque utilisateur), transferts de fichiers.
  • Changer l’ID d’un client
  • Utiliser un carnet d’adresse qui sera synchronisé entre les clients de chaque utilisateur, mais non accessible à l’admin.
  • Appliquer des stratégies
  • Et d'autres fonctionnalités à venir car le projet est en constante évolution....

Il n'existe pour l'instant pas de version Cloud et je n'ai pas le sentiment que ce soit dans l'ordre des priorités de l'équipe.

Installation

Je me place ici dans le cadre de la version serveur après avoir acquis une licence.

L'installation du serveur se fait sous Linux ou dans un container Docker. La procédure est décrite ici et je ne vais pas la reprendre. J'ai choisit Docker pour la facilité d'un Compose...

Coté client, contrairement à TeamViewer qui fonctionne uniquement avec une infrastructure cloud, RustDesk fonctionne avec une infrastructure privée si l’on veut disposer de toutes ses fonctionnalités. Quand on installe le client il va donc falloir configurer les informations liées au serveur. Ce device sera alors uniquement accessible par des clients également configurés pour ce serveur (tout au moins pour l’instant car il y a des évolutions en cours).

Pour configurer le client afin qu’il soit reconnu par le serveur on a plusieurs solutions :

  • Soit on installe l’exe normalement et on copie la config dans ID / Relay server
Qfi0TWlxGWrUdfqdsfVENxxxxxxxxxxxxxxxxxxxxxxxqfsqdfN3boJye

  • Soit on renomme l’exe d’installation afin d’y intégrer la clé, ce qui va donner quelque chose du genre:
rustdesk-licensed-Qfi0TWlxGWrUkR5Vxxxxxxxdfsdfsdfsdfye.exe
  • Soit on crée un script de déploiement et ainsi appliquer la configuration après l’installation dans le cadre d’un script PowerShell, on dispose alors de ces trois commandes, et on verra plus bas ce à quoi peut ressembler un script de déploiement  :

Se connecter

En général quand on utilise ce genre de produit, souvent pour de l'assistance client ou de la gestion de serveurs, il y a deux types d'installations :

  • Le device client, le mien, sur lequel après avoir configuré le serveur (chapitre précédent), je vais me connecter avec l'identifiant fournit par l'administrateur du serveur : 
  • Le device distant auquel on va affecter (ou pas) un utilisateur

Affecter un device à un utilisateur

Si on affecte un device à un utilisateur, cet utilisateur (et uniquement lui) verra ce device distant monter dans son logiciel client. Il pourra également l’ajouter à son carnet d’adresse personnel. Si on ne l'affecte pas, tous les utilisateurs le verront, pourront l'ajouter à leur carnet d'adresse et s'y connecter (sous réserve de disposer du mot de passe préalablement défini).

Des fonctionnalités de groupe et de partage de groupe sont également prévues. Il est ainsi possible de partager les devises affectés à un groupe à un autre utilisateur. A noter que pour l'instant groupe = utilisateur et que l'on ne peut pas directement affecter des devices à un groupe isolé. La solution consiste donc à créer un utilisateur virtuel qui partagera ses devices avec d'autres utilisateurs. Par exemple j'ai créé un utilisateur virtuel Société A qui partage ses devices avec les deux personnes chargés de la maintenance de Société A. Ca consomme une licence utilisateur mais je n'ai pas trouvé mieux pour l'instant.

Il existe plusieurs façons pour y parvenir :

  • En se connectant avec son user/password (mais c’est une très mauvaise idée que de faire ça sur le PC d’un client)
  • En l’associant dans l’interface d’administrateur (mais seul l’admin peut le faire)
  • En utilisant l’API sur le client dans le cadre d’un déploiement vie un script

Pour ça il faut un token. Les tokens qui permettent l’accès à l’API sont délivrés par l’admin.

PS C:\Program Files\RustDesk>.\RustDesk.exe  –assign –token "rapi_xxxxxxxxdyw==" –user_name “admin” | more

La console

Chaque utilisateur peut voir la liste de ses devices, l’état de la connexion ainsi que diverses infos (par exemple :

ip: 82.65.xxx.xxx;
version: 1.2.3;
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz, 2.53GHz, 4/4 cores; mem: 8GB

Un log est également disponible tant pour les connections que pour les transferts de fichiers. Cette console est destinée à évoluer pour plus de fonctionnalités.

L'administrateur peut quant à lui, outre gérer utilisateurs et devices, affecter des stratégies aux utilisateurs et devices :

Script PowerShell d’installation

On ne dispose pour l'instant pas de .msi et ce script est un peu simpliste (il en existe pour d'autres O/S ici). Pour faire simple je conseille d’ouvrir une session PS en mode admin. Mais il est également possible de faire une élévation dans le script (voir à la fin). Pour l’instant attention à indiquer la version de l'exe sur la bonne ligne…

################################# VARIABLES #######################################
$rustdesk_pw="PasswordPermanent"  # Celui du device distant
$rustdesk_token="MonTocken"
$rustdesk_cfg="Qfi0TWlxxxxxxxxxxxxxxxxxxxxxxxxxxN3boJye" # Clé de configuration
$ErrorActionPreference= 'silentlycontinue'
################################### SCRIPT ########################################

If (!(Test-Path C:\Temp))
{
    New-Item -ItemType Directory -Force -Path C:\Temp > null
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
cd C:\Temp
Invoke-WebRequest "https://github.com/rustdesk/rustdesk/releases/download/1.2.3/rustdesk-1.2.3-x86_64.exe" -Outfile "rustdesk.exe" # Changer la version le cas échéant

Start-Process .\rustdesk.exe --silent-install # -wait
$ServiceName = 'Rustdesk'
$arrService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue

if ($arrService -eq $null)
{
    Start-Sleep -seconds 20
}
cd $env:ProgramFiles\RustDesk\
$rustdesk_id = (.\RustDesk.exe --get-id | out-host)

.\RustDesk.exe --config $rustdesk_cfg | out-host
.\RustDesk.exe --password $rustdesk_pw | out-host
Start-Sleep -Seconds 5
.\RustDesk.exe --assign --token $rustdesk_token --user_name manu | out-host

Elévation (le cas échéant à ajouter au début du script)

if (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
        Start-Process PowerShell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"";
        Exit;
    }
}

Le cas des serveurs Windows et du RDP

Si on installe RustDesk sur un serveur Windows va se retrouver face à deux possibilités, activer ou pas le partage de session RDP (une problématique identique existe avec TeamViewer qui a choisit de fournir deux ID, cela concerne principalement les administrateurs de serveurs, qu’ils soient premise ou cloud) :

  • Non activé : RustDesk va proposer une ouverture de session, il est possible d’enregistrer le mot de passe dans le client, et cette ouverture de session déconnectera l’éventuel utilisateur connecté en RDP.
  • Activé : Dans ce cas RustDesk partage la connexion RDP active et si on déconnecte la session RDP ça déconnecte la session RustDesk (qui peut toutefois se reconnecter s’il dispose des identifiants Windows.
    • Il existe toutefois un problème, dans ce mode rien ne permet actuellement de choisir la session sur laquelle va s’opérer le partage, il y a donc un risque de se concerter sur la session d’un utilisateur actif dans le cas d’un serveur de bureaux virtuels. (ticket ouvert en cours).

Conclusion

RustDesk est constante évolution et j'y reviendrait certainement ici. En attendant j'ai déployé une trentaine de devices sans observer de problèmes particulier si je compare à mon classique TeamViewer.

RDP & Guacamole

Et non, je ne suis hélas pas parti en vacances au Mexique déguster du guacamole arrosé de téquila ! Mais j'ai trouvé la solution que je cherchait depuis longtemps pour sécuriser les accès RDP. Comme chacun le sait il n'est vraiment pas conseillé de laisser ouvert sur internet le port RDP qui est plutôt vulnérable. Hélas pensant le confinement beaucoup de clients ont du mettre en place des solutions dans l'urgence et dans bien des cas il était impossible de verrouiller au minimum ce port sur une IP fixe....

Bien sur Microsoft a une solution (RDP Gateway), couteuse et complexe, donc pas adaptée à de petites entreprises dont les couts IT explosent déjà. Je restait donc en éveil à la recherche d'une solution quand je suis tombé sur le projet open-source Guacamole en HTML5. Et surprise, celui ci fonctionne vraiment bien ! Le principe est simple et reprends celui des autres projets, une machine va servir de proxy afin d'exposer le RDP (mais aussi SSH, Telnet et VNC).

Installation

S'il est possible d'installer Guacamole en natif comme c'est très bien expliqué ici, après avoir testé différentes options, par facilité je vais faire ça sous Docker en utilisant ce projet.

Je vais donc monter une VM Linux Ubuntu et y configurer Docker et Docker Compose. Pour cette partie je vous laisse chercher, d'autres expliquent mieux que moi.

Pour déployer Guacamole on a besoin de 3 services :

  • GUACD : Le cœur de Guacamole qui va servir à connecter les différents services (RDP, SSH, etc...)
  • PostgreSQL : Une base de donnée (qui peut être également MySQL ou encore l'utilisation d'une base externe)
  • Guacamole : Le composant FrontEnd qui va gérer les connections, les services et les utilisateurs.

Docker Compose

L'auteur de l'intégration a eu la bonne idée de nous fournir un script d'installation. On va donc l'utiliser et on se servira de Docker Compose plus tard afin d'ajuster la configuration.

sudo git clone "https://github.com/boschkundendienst/guacamole-docker-compose.git"
cd guacamole-docker-compose
./prepare.sh
sudo docker-compose up -d

Si tout se passe bien on peut se connecter sur https://ip_serveur:8443/guacamole. Le nom d'utilisateur par défaut est guacadmin avec mot de passe guacadmin. La première chose à faire est bien sur de le changer.

On pourra facilement configurer un serveur RDP et s'y connecter pour avoir les premières impressions. Je trouve que le rendu RDP en HTML5 est fluide, même en regardant des vidéos sur YouTube à une bonne résolution. Attention à ne pas perdre de vue que c'est le serveur Guacamole qui doit encaisser le trafic entrant et sortant. Selon l'usage et le nombre de clients il faudra donc le dimensionner correctement.

Je ne vais pas m'étendre sur les différentes options, l'interface et claire, la documentation également et d'autres en parlent très bien.

Sécurisation

Il n'est bien sur pas question d'exposer ce serveur directement sur internet et je vais tester deux solutions de reverse proxy, je commence par éditer le fichier docker-compose.yml afin de supprimer le proxy Nginx préinstallé histoire de ne pas empiler les proxys. J'ajuste également en 8080:8080 pour une utilisation en direct et REMOTE_IP_VALVE_ENABLED: 'true'  pour activer le proxy externe et WEBAPP_CONTEXT: 'ROOT' afin que Guacamole soit accessible en racine :

version: '2.0'
networks:
  guacnetwork_compose:
    driver: bridge

services:
  # guacd
  guacd:
    container_name: guacd_compose
    image: guacamole/guacd
    networks:
      guacnetwork_compose:
    restart: always
    volumes:
    - ./drive:/drive:rw
    - ./record:/record:rw
  # postgres
  postgres:
    container_name: postgres_guacamole_compose
    environment:
      PGDATA: /var/lib/postgresql/data/guacamole
      POSTGRES_DB: guacamole_db
      POSTGRES_PASSWORD: 'ChooseYourOwnPasswordHere1234'
      POSTGRES_USER: guacamole_user
    image: postgres:15.2-alpine
    networks:
      guacnetwork_compose:
    restart: always
    volumes:
    - ./init:/docker-entrypoint-initdb.d:z
    - ./data:/var/lib/postgresql/data:Z

  guacamole:
    container_name: guacamole_compose
    depends_on:
    - guacd
    - postgres
    environment:
      REMOTE_IP_VALVE_ENABLED: 'true'   # On active ici l'utilisation via un proxy externe
      WEBAPP_CONTEXT: 'ROOT'
      GUACD_HOSTNAME: guacd
      POSTGRES_DATABASE: guacamole_db
      POSTGRES_HOSTNAME: postgres
      POSTGRES_PASSWORD: 'ChooseYourOwnPasswordHere1234'
      POSTGRES_USER: guacamole_user
    image: guacamole/guacamole
    volumes:
    - ./custom/server.xml:/home/administrator/guacamole-docker-compose/server.xml
    links:
    - guacd
    networks:
      guacnetwork_compose:
    ports:
    - 8080:8080
    restart: always

Je relance docker :

administrator@guacamole:~/guacamole-docker-compose$ sudo docker-compose up -d

En local je me connecte maintenant en http://ip_serveur:8080 sans SSL car le SSL sera géré par le proxy.

Option 1 : pfsense

J'ai un pfsense installé avec HAProxy et Acme pour gérer les certificats Lets'Encrypt, je vais donc me servir de ca pour publier le service. Sur HAProxy on configure le BackEnd et le FronteEnd qui lui utilisera le certificat préalablement créé avec Acme. Dans ma configuration je partage l'IP avec plusieurs sites et ce qui est important c'est d'activer l'option forwardfor qui permettra de transférer les adresses sources à Guacamole.

Je mets ici le code de configuration qui sera utile à ceux qui n'utilisent pas HAProxy sous pfsense qui lui dispose d'une interface graphique. Comme on peut le constater le HTTP to HTTPS se fait au niveau du HAProxy et c'est lui également qui redirigera les requetés HTTTP vers HTTPS et mon serveur sera accessible sur https://guacamole.mondomaine.tld :

global
  maxconn     10000
  stats socket /tmp/haproxy.socket level admin  expose-fd listeners
  uid     80
  gid     80
  nbproc      1
  nbthread      1
  hard-stop-after   15m
  chroot        /tmp/haproxy_chroot
  daemon
  tune.ssl.default-dh-param 1024
  server-state-file /tmp/haproxy_server_state

listen HAProxyLocalStats
  bind 127.0.0.1:2200 name localstats
  mode http
  stats enable
  stats admin if TRUE
  stats show-legends
  stats uri /haproxy/haproxy_stats.php?haproxystats=1
  timeout client 5000
  timeout connect 5000
  timeout server 5000

frontend Shared_WAN-merged
  bind      x.x.x.x:443 (IP WAN) name x.x.x.x:443 (IP WAN)   ssl crt-list /var/etc/haproxy/Shared_WAN.crt_list  
  mode      http
  log       global
  option    http-keep-alive
  option    forwardfor
  acl https ssl_fc
  http-request set-header   X-Forwarded-Proto http if !https
  http-request set-header   X-Forwarded-Proto https if https
  timeout client    30000
  acl     Admin var(txn.txnhost)      -m str -i admin.domain.tld
  acl     guacamole var(txn.txnhost)  -m str -i guacamole.domain.tld
  http-request set-var(txn.txnhost) hdr(host)
  use_backend Admin_ipvANY  if  Admin 
  use_backend Guacamole_8443_ipvANY  if  guacamole 

frontend http-to-https
  bind            x.x.x.x:80 (IP WAN) name x.x.x.x:80 (IP WAN)
  mode            http
  log             global
  option          http-keep-alive
  timeout client  30000
  http-request redirect code 301 location https://%[hdr(host)]%[path]  

backend Admin_ipvANY
  mode     http
  id       100
  log      global
  timeout  connect   30000
  timeout  server    30000
  retries  3
  server   admin 192.168.55.44:80 id 101  

backend Guacamole_8443_ipvANY
  mode     http
  id       102
  log      global
  timeout  connect   30000
  timeout  server    30000
  retries  3
  server   guacamole 192.168.66.55:8080 id 101

Option 2 : CloudFlared

Cette option est encore plus simple pour ceux qui disposent d'un domaine chez Cloudflare. On va utiliser les possibilités offertes gratuitement par Cloudflared et ajouter une couche de sécurité supplémentaire.

On commence par créer un tunnel CloudFlared avec une instance Docker supplémentaire (le code est fournit par le site de configuration Zero Trust de Cloudflare)

docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiZjk0YjdkZTBiMzFmYWNkNjZlshhsgfhsfghsgfhgfhssgfhsfgzRiMC00MmNlLWJjMghsfghsghsgfhsgfhsgfhsgmpaR00tyyretu(-yenebybvewWXpGaUxUazVNell0TnpRek56WXhNMlkxWXpFeiJ9

Ensuite une fois que le tunnel est monté

On va publier et pointant sur l'ip privée et le port de notre serveur, cela va automatiquement créer l'entrée DNS dans CloudFlare et gérer la problématique du certificat. Ll sera accessible en SSL. :

Ensuite on va ajouter une couche de sécurité supplémentaire en créant une application de type SelfHosted à laquelle on affecte une policie qui impose un code supplémentaire lors d'une connexion. Ce code sera envoyé sur le mail de l'utilisateur sur une adresse individuelle ou un domaine spécifique. Ce n'est pas tout à fait du MFA, mais on considère que si l'utilisateur reçoit bien le code sur son mail professionnel, il s'agit bien de lui et on peut lui permettre de saisir son login / password pour accéder au service :

C'est la méthode la plus simple et de nombreuses option sont exploitables, de même qu'il est possible d'utiliser de nombreux providers externes d'authentification :

Lorsqu'il souhaite se connecter, l'utilisateur tombe sur un portail que l'on peu personnaliser aux couleurs de l'entreprise.

Voilà, et surtout maintenant on retrouve bien les IP publiques dans le log de Guacamole :

Il n'y a plus qu'a ajuster les différentes options de Guacamole et pour ça la documentation est très bien faite. Et bien sur si on se sert de Guacamole pour exposer des serveurs RDP accessibles sur l'internet on prendra soin de restreindre leur usage à l'IP publique du serveur Guacamole, ou mieux de faire transiter ce flux par un tunnel (Wireguard, Tailscale ou Zerotier par exemple que l'on peu facilement installer sur les deux serveurs).

Tailscale Access Controls

J'ai souvent parlé de Zerotier que j'utilise au quotidien, mais qui dans sa version gratuite comporte quelques limitations en terme de contrôle d'accès dans un environnement mixant du site-to-site et des clients isolés. J'ai donc voulut voir ce que proposait Tailscale, qui est un VPN managé relativement similaire reposant sur Wireguard. Tous deux offrent des fonctionnalités gratuites et payantes. Si payer ne doit pas être un problème dans un environnement de production, c'est un peu différent en mode home lab.... d'autant plus que certaines fonctionnalités de la version gratuite ne se retrouvent que dans la version Premium à $18 /mois/utilisateur.

La version gratuite des Tailscale est plutôt généreuse, elle propose 100 nodes et 3 utilisateurs (à condition de disposer d'un "custom domain" (je vais utiliser des comptes Microsoft 365, mais plusieurs autres possibilités sont disponibles : SSO Identity Providers). On peut donc disposer de 3 comptes sur un même domaine. Je parle de comptes car nativement (et normalement) le contrôle d'accès se fait sur l'utilisateur. Manque de chance j'ai besoin de gérer 4 utilisateurs, et je ne suis pas prêt à payer 4 x $18, ce qui me ferait plus de $ 800 par an !

J'ai donc cherché à contourner et je vais finalement gérer mes autorisations à partir des machines Tailscale que j'ai approuvées (mode NFS pour ceux qui se souviennent). Dans cet usage je ne cherche pas faire communiquer les clients Tailscale entre eux mais à leur permettre d'accéder à des ressources situées sur différents subnets sur lesquels je souhaite restreindre les possibilités.

Pour la suite on considère que le réseaux Tailscale est configuré avec les nodes qui servent à joindre les subnets.

Je vais créer deux utilisateurs :

[email protected]
[email protected]

Le premier compte qui sera également le mien va également me permettre de configurer et approuver les machines clientes.

Le second servira à connecter mes utilisateurs et je pourrais approuver leurs machines. Ce compte étant protégé en MFA, il nécessitera une validation de ma part. Une fois la machine connectée, je désactive l'expiration de la clé et on passe au contrôle s'accès :

{
  "acls": [
    {"action": "accept", "src": ["[email protected]"], "dst": ["*:*"]}, // Ici l'admin dispose de tous les droits
    
    // Autorisation par machines
    {
      "action": "accept",
      "src": ["azura", "lydia"],      // Ici deux clients Tailscale
      "dst": [
        "pipa:25,53",                 // On autorise le host qui supporte le DNS si on veut utiliser les noms courts...
        "mixa:80,443,445,3389",
        "nona:80,443,445,1443,3389",  // Ports autorisés

        "lab-1:*",

        "192.168.2.12:80,443,9100,5001,3389", // Par IP + ports
        "192.168.6.0/24:*",                   // Par subnet
      ],
    },
  ],
  "hosts": {
    "azura": "100.1.2.3",        // Client Tailscale
    "lydia": "100.5.2.3",        // Client Tailscale

    "mixa":  "192.168.1.4",      // Host on subnet
    "nona":  "192.168.2.8",      // Host on subnet
    "pipa":  "192.168.2.1",      // Host on subnet

    "lab-1": "192.168.6.0/24", // subnet
  },

}

Dans l'ordre :

  1. J'autorise l'admin à tout voir.
  2. Ensuite j'autorise les machines azura et lydia à accéder uniquement aux hosts que j'aurais défini avec une restriction sur certains ports. C'est possible en définissant les hosts plus loin ou en se servant directement des adresses IP. L'ICMP (ping) est ouvert automatiquement vers les destinations autorisées.
  3. Je défini les clients et serveurs.

Voilà. Je n'ai pas creusé toutes les possibilité offertes par le contrôle d'accès proposé par Tailscale, la liste est longue et le documentation plutôt bien faire. Idées bienvenues !

Sony mon amour !

Amour déçu comme on le verra plus loin ! Depuis très jeune j'ai toujours été un afficionado de la marque Sony. Et si d'aucune pourraient trouver cela déplacé, il est des domaines ou je suis fidèle ! Coté TV après avoir eu quelques Trinitrons cathodiques célèbres tel le Profeel, je me suis offert jadis le premier moniteur plasma pro de la marque, qui avait du me couter un an de salaire et que je me suis résolu récemment à le porter à la déchetterie, le pauvre il trainait dans le garage depuis bientôt 20 ans !

Je ne vais pas vous faire la liste des écrans Sony que j'ai eu successivement, ni leur cout car ça me déprimerait, mais juste vous parler des déboires de mon dernier TV. Il y a un an j'ai craqué pour le A80J, un Oled ! Un TV intelligent sous Google TV, même si d'intelligence il n'a que la pub et les traqueurs que cela induit !

Je considère Google TV / Android TV comme étant le meilleur choix car il me permet d'installer les application qui me sont utiles, en opposition aux écosystèmes fragmentés de la concurrence qui ne proposent que l'essentiel bien commercial ! C'est du business, on le sait et on l'accepte car ça nous apporte du confort.

Autres points que les pseudo journalistes testeurs oublient souvent, c'est la télécommande et l'implémentation du CEC. Celles de Sony m'ont toujours satisfait alors que je déteste certaines d'autres marques. Mais Sony est radin, et sur le A80J la télécommande n'est pas même rétroéclairée ! Qu'à cela ne tienne il suffit de commander sur Amazon celle du A90J qui elle est premium et rétro éclairée... Quant au CEC il a toujours été très bien chez Sony.

Je ne vais pas vous raconter ce que vaut cette TV, plusieurs sites l'ont déjà fait plus ou moins bien. Enfin, je parle de TV, mais je ne regarde même pas la télé et aucune antenne n'y est raccordée. Je rêve d'une version moniteur !

Domotique

Oh le gros mot ! Encore un client qui veut faire des choses louches ! Louches car je ne me plie pas à la domotique commerciale et forcément limitative (Amazon, Google, Apple, Tuya, etc...) mais que ma domotique est libre, Home Assistant.

Et pourquoi faire ? Pas grand chose, juste inclure ce TV dans me scénarios (scènes). Et on va faire simple !

  • Play / Pause : on éteint et on rallume progressivement les éclairages comme au cinéma.
  • On / Off : on mets sur pause la musique qui était diffusée.

Ce n'est pas sorcier et très simple à faire. Mais pour ca il faut pouvoir extraire les informations du TV. Ca tombe bien ce TV est reconnu sous Home Assistant par l'intégration Bravia ou HomeKit. Dès lors on dispose des information idoines et ça fonctionne très bien, il suffit d'agir en fonction de l'état remonté.

Mais alors il se plaint de quoi le râleur ? Et oui il y a un "mais" !

Consommation

Pour accéder à ces fonctions il faut que le contrôle à distance ou / et HomeKit soit activé. C'est d'ailleurs le cas par défaut. Et les ingés de chez Sony, dont je ne doute pas des prouesses en audio / vidéo s'avèrent des brèles en réseau ! Pour que ces fonctions soient accessibles ils laissent simplement la partie réseau allumée quand le téléviseur est en veille. Et comme leur carte réseau doit dater du siècle dernier (elle a du mal à accrocher le réseau et est limitée à du 10/100 en 2022 ou on est censés passer de la full 4K !), elle consomme en veille pas moins de 26/28 W alors que si ces fonctions sont désactivées la veille est à seulement 0.9 W.

Cela pose deux constats :

  • Il est inacceptable qu'un TV en veille consomme 27 W en 2022 (c-a-d + ou - 50 € / an).
  • Il est inacceptable que ces fonctionnalités soient activées par défaut et induisent cette surconsommation ! Et certainement contraire aux directives européennes en la matière. Il est en effet peu probable qu'un consommateur lambda ne pense à mesurer et désactiver ces fonctions.

En l'état il est donc inenvisageable d'utiliser ces fonctionnalités qui ajoutent un plus au téléviseur. Et il faut noter que tout pilotage par les applis standard, Chromecast, Alexa, etc pose le même problème...

Le pire étant que si on désactive ces fonctions, quand on allume le téléviseur il mets bien trop longtemps pour accrocher le réseau, que ce soit en Ethernet ou en WI-FI. Je sais c'est fait pour regarder la télé avec un râteau sur la toiture !

Alternative

Avant j'utilisait un LCD Sony bien plus bête et une NVidia Shield que je pilotait très bien sans que cela ne grève mon budget. Le Shield allumait le TV en CEC et tout était parfait. Je vais peut être en racheter un, encore que mon Shield de 2015 fonctionne encore très bien dans ma chambre. Un bel exemple de non obsolescence qui mérite d'être signalé !

Et les autres ?

Je ne sais pas comment ça se passe chez les autres constructeurs de TV car je ne suis pas client. On me remonte 1 à 2 W avec les options de pilotage activées chez Samsung ou Panasonic, mais je n'ai pas vérifié. Par contre j'ai un bel élevage de Sonos, et là non plus ce n'est pas très joli à voir : aucun progrès depuis le début, conso identique en veille sur les anciens Play 1 et 3 et les nouveaux One (gen 1 et 2), entre 3 et 4 W voire plus de 8 à 11 W sur un AMP ou 6 W pour une Beam !

Moralité

La domotique n'est pas vraiment écologique, elle crée un énorme talon de consommation de part son infrastructure (caméras, nvr, switches, micro modules et autres prise connectées). Elle apporte plus de confort qu’elle ne fait réaliser des économies (les économies c’est le bobard que racontent certains à leur compagne quand c’est elle qui tiens les cordons de la bourse, car, hélas, la domotique reste un truc de mec, comme le BBQ, n’en déplaise à Sandrine).

Un panneau solaire va devoir s'imposer à moi !

Sources

 

 

Home Assistant & Wake on Lan

L'heure est aux économies d'énergies et il parait qu'il faut éteindre "la" Wi-Fi ! Ce n'est peut être pas ce qui consomme le plus et notre domotique fait certainement mieux.... Par contre nombre d'entre nous ont des PC énergivores qui mériteraient de passer en veille. Sauf que dans la pratique on laisse souvent ce PC allumé en se disant que l'on pourra en avoir besoin depuis l'extérieur... Ce que j'ai longtemps fait !

Un PC moderne avec un SSD sait sortir de veille en quelques secondes. Alors pourquoi ne pas ressortit une vieille fonctionnalité très peu utilisée, le Wake on Lan.

Sous Linux, Windows, voire même les mobiles il existe des application GUI ou CLI. Il faut tout de même savoir que Wake on Lan n'a pas été conçu à l'époque pour fonctionner à distance, et même s'il existe des possibilités ça reste compliqué. Alors pourquoi ne pas simplement utiliser Home Assistant qui lui sera toujours disponible, voire même simplement ajouter ça à un ESP existant, sous ESP Home par exemple..

La première chose pour vos tests sera de vérifier en local que le Wake on Lan fonctionne (il y a pas mal d'utilitaires et d'explications sur ce site).

Avec Home Assistant

On commence par ajouter l'intégration au fichier de configuration et on redémarre :

wake_on_lan:

Ensuite on peur créer un switch: , mais aujourd'hui on préfèrera un button: dans Lovelace qui actionnera le service correspondant :

  - action_name: 'Sleep/UnSleep'
    double_tap_action:
      action: call-service
      service: button.press
      target:
        entity_id: button.lia_monitors_off
      action: call-service
      confirmation:
        text: Etes vous sur ?
    tap_action:
      action: call-service
      service: wake_on_lan.send_magic_packet
      data:
        mac: 00-0C-29-FB-77-97
    icon: mdi:microsoft-windows
    name: 'Windows 10 PC'
    type: button

Vous remarquerez que j'ai configuré ça sur le tap_action:, le double_tap_action: étant lui configuré pour la commande de mise en veille via Home Assistant Agent.

Avec ESP Home

J'ai un client full cloud qui dispose d'une douzaine de PC, mais pas de serveur et encore moins de Home Assistant. La première solution était de laisser un PC allumé et y installer de quoi faire du Wake on Lan en remote. Il y a également des possibilités via TeamViewer et hélas pas de base dans l'UDM Pro d'Ubiquiti. On verra ce l'on utilisera mais j'ai également pensé faire ça via un simple ESP qui lui pourrait resté allumé...

Dans ESP Home on va éditer la config et simplement ajouter (attention, ici les séparateurs ne sont pas des "-" mais ":") :

web_server:    

button:
- platform: wake_on_lan
  name: "VM Annia"
  target_mac_address: 00:0C:29:FB:77:97

A partir de là il est possible de presser notre button: qui remonte dans Home Assistant, sur la page web de l'Esp, mais également via la commande curl (la doc est ici) :

curl -X POST http://192.168.210.153/button/vm_annia/press

Et comme on ne va pas demander à l'utilisateur final de faire un "curl" on va lui emballer tout ça dans une page web accessible que l'on sécurisera afin que lui seul puisse y accéder, en partant de cette base :

<HTML>
<center>

	<form name="myform" action="http://192.168.210.153/button/vm_annia/press" method="post">
  	<button>PC Annia ON</button>
	</form>

</center>
</HTML>

Variante avec un WebHook

L'ESP remonte sur Home Assistant via ESPHome. De fait on peu presser un bouton pour réveiller le PC distant, voire même intégrer ce bouton sur un autre Home Assistant en remote. Mais il est également possible de créer un WebHook et de déclencher avec un raccourcis depuis le PC de l'utilisateur distant :

- alias: Réveil PC Carole (WebHook)
  description: ""
  trigger:
    - platform: webhook
      webhook_id: "secret-id"
  condition: []
  action:
    - service: button.press
      data: {}
      target:
        entity_id: button.reveil_pc_carole
  mode: single

Et le raccourcis à créer sur le PC de l'utilisateur qui doit réveiller son PC de bureau...

curl -X POST https://ha-online.suptel.org/api/webhook/<secret-id>

Infos

  • Attention, Microsoft a introduit un concept de veille moderne qui souvent demandera à être désactivé. Des infos ici et .
  • Il n'est pas possible de réveiller un PC connecté en Wi-Fi ou en Ethernet via USB.
  • A noter que la Freebox dispose d'une option permettant de laisser passer du Wake on Lan. Ce n'est pas le cas pour tous les routeurs et il faut parfois ruser avec le port 9.

Sources

 

Home Assistant & Cloudflare Zero Trust

Une fois de plus on va parler de VPN. J'avais ici évoqué Zerotier (gratuit pour 25 nodes) que j'utilise toujours notamment pour interconnecter plusieurs sites entre eux en remplacement d'IPSEC. Ca fonctionne très bien et ça se fait oublier. Entre temps on a découvert Wireguard qui est très performant et peut être utilisé en natif (faut faire le taff) ou via des intégrations comme Tailsacle (entre autres) dont la version gratuite sera suffisante pour bien des usages.

Aujourd'hui on va parler de Cloudflare Zero Trust. Au départ je voulais juste tester sous Home Assistant car cela permet de publier son Home Assistant sans ouvrir de ports sur le routeur, de la même façon que l'on peut le faire si l'on dispose d'un abonnement Nabu Casa. J'ai cet abonnement, mais je ne veux pas l'imposer aux utilisateurs que j'aide.

Si Zero Trust est basé sur Wireguard il n'a rien d'open source. Certains n'aimeront pas quelque chose qui passe par Cloudflare qui comme beaucoup d'acteurs du marché pratique la collecte de donnée. C'est le business de l'époque, d'autres font pire mais ce n'est pas le débat ici, alors épargnez moi vos digressions sur ce sujet, ce n'est pas l'objet. Si vous choisissez cette solution c'est en connaissance de cause. Il existe des alternatives, moins simples à mettre en œuvre.

Zero Trust est un VPN orienté client qui permet notamment deux approches :

  • Publier et sécuriser, sans ouvrir de ports, un (ou plusieurs) site hébergés en premise (premise = chez vous, dans votre entreprise) et le rendre accessible publiquement avec des restrictions qui assureront sa sécurité (MFA) et surtout des restrictions géographiques qui sont quasi impossibles avec les autres solutions, notamment à cause de Let'Encrypt.
  • Rendre accessible des réseaux privés (VPN) en passant par le client Warp de Cloudflare) et ainsi accéder à toutes les machines du réseau (RDP, SMB, SSH, etc...).

Ce qui fait la force de cette solution ce sont les policies qui permettent une très grande granularité. Cloudflare Zero Trust est gratuit jusqu'à 50 utilisateurs. Et dans l'absolu pour sécuriser Home Assistant on a même pas besoin du moindre utilisateur.

Home Assistant

Un Add-on est disponible ici et sa mise œuvre est 'une simplicité enfantine mais il y a une petite subtilité. En effet il y a deux façons pour gérer un tunnel :

  • En local et en CLI
  • Depuis le dashboard de Zero Trust

On part du principe que vous avez un compte Cloudflare et qu'un de vos domaines y est géré. Vous avez également activé Zero Trust. A faire avant toute chose.

Dans les deux cas on ajoute ou modifie le fichier de configuration de Home Assistant ainci :

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.30.33.0/24

Option 1, gestion en local

On installe l'add-on sur Home Assistant, on choisit un host pour Home Assistant, ha.domaine.com par exemple et on lance l'add-on. Il suffit simplement ensuite d'aller copier dans le log l'url de validation et de la copier dans votre navigateur.

Et ça fonctionne, installation du certificat intermédiaire comprise. On pourra ensuite sécuriser et jouer avec les options, la doc de l'add-on est ici et cette  de Zero Trust . Si vous ne pensez pas utiliser Zero Trust pour autre chose c'est la solution la plus simple.

Option 2, gestion en remote

Vu que je compte utiliser Zero Trust à d'autres fin, c'est l'option que j'ai choisit.

  • Dans la console (Access/Tunnels) je crée un nouveau tunnel que ne configure pas.
  • Je copie le token que je viens reporter dans l'add-on et je lance l'add-on
  • Je vais dans le log et je copie l'url de validation que je colle dans le navigateur afin de finaliser l'installation et valider le certificat. 
  • Dans la gestion du tunnel je vais créer un Public Hots Name qui va correspondre à mon serveur et sera exposé avec le nom de domaine choisit :

Votre Home Assistant est maintenant accessible, ici https://test-ha.canaletto.fr (sans le port).

Aller plus loin...

En premier lieu je vous conseille de définir une policie dans Access/Applications afin que votre serveur ne soit accessible que depuis votre pays et surtout pas depuis des états plein de black hat's ...

Ensuite il faut savoir que vous avez installé sur votre serveur une passerelle VPN qui va vous permettre d'accéder à tout votre réseau local en passant par le client Warp. Pour cela il faut dans la configuration du tunnel déclarer un Private Network.

Ensuite dans les SettingsGeneral on va définir le nom de (Team) :

Dans Settings/Network on va changer le mode des tunnels. De base, dès lors que le client Warp est activé, Zero Trust va faire passer tout ce qui ne ressemble pas à une adresse privée dans ses tuyaux, c-a-d tout votre trafic, probablement à des fins de collecte de donnée... On va donc remplacer le mode Exclude par le mode Include et y déclarer uniquement notre réseau privé. Ainsi seul le trafic tunnelisé transitera par Cloudflare, et comme il est dans un tunnel ce sera théoriquement incognito. Je dis théoriquement car dans cette solution ce n'est pas vous mais Cloudflare qui a les clés... Mais nous sommes dans le cadre d'un service de classe entreprise et les CGU garantissent la confidentialité des données...

Dernier point, déclarer une méthode d'authentification. De base une authentification par code PIN est proposée, vous déclarez un domaine ou une adresse mail et vous revenez un code PIN à 6 chiffres qu'il suffit de rentrer... En option il est possible de configurer un SSO en utilisant une authentification que l'on exploite déjà (Azure AD, Centrify, Facebook, GitHub, Google Workspace, Google, LinkedIn, Okta, OneLogin, Saml, OpenID Connect, etc...). Plus complet, mais ça peut répondre à certains besoins en entreprise.

A partir de la on installe le client et on se connecte avec le nom que l'on a défini plus haut. On teste un RDP, SMB ou SSH sur une IP du réseau privé, et ça marche. Ca veut dire qu'à ce stade tout est ouvert dès lors que l'on a connecté le client, pourquoi pas dans le cadre d'une utilisation personnelle, mais je ne saurait trop vous conseiller de tout interdire et de n'autoriser que ce qui est utile (Gateway/Policies/Network).

Cet outil étant avant tout destiné à une utilisation en entreprise les possibilités sont immenses. Pour autant l'administration n'est pas très compliquée, avec quelques connaissance de base dans la gestion des réseaux.

Echanger

J'ai créé un sujet sur HACF, plus pratique qu'ici pour échanger.

 

Bibliothèque Sonos

L'écosystème Sonos a bien évolué au fil des années, avec du positif comme du négatif ( 1 | 2 ), mais tout en intégrant une multitude de services musicaux, Sonos a toujours délaissé, le nombre de fichiers musicaux locaux explorables. En l'état cette limite est toujours fixée à 65.000 fichiers alors même que l'évolution Sonos 2 permettrait certainement de s'en affranchir.

J'ai longtemps utilisé Subsonic (ou ses forks) qui permet de contourner cette limite. Mais d'une part Subsonic est payant, et surtout n'est plus maintenu.

Aujourd'hui il existe un nouveau fork open source, Navidrome, qui est compatible avec les clients Subsonic, mais hélas ne propose pas de compatibilité Sonos. Fort heureusement un autre développeur de génie a eu la bonne idée de mettre à disposition une interface, Bonob, via les API Sonos, qui va permettre de faire le lien entre Sonos et les fors de Subsonic, en l'occurrence ici Navidrome.

Si ces programmes sont installables sous Linux, Windows ou MacOS, je vais choisir la facilité en passant par Docker. Pour y parvenir je commence par installer une petite VM Ubuntu Serveur avec Docker installé, et comme mes fichiers musicaux sont sur mon Nas je vais le lier en créant un volume NFS.

On commence par installer les paquets NFS :

administrator@vm:~# sudo apt install nfs-common

Ensuite on crée le répertoire idoine et on le lie au Nas :

administrator@vm:~# sudo mkdir -pv /nas/Music
administrator@vm:~# sudo mount 192.168.0.241:/volume1/Music /nas/music

Et pour terminer cette partie on fige le montage NFS en éditant le fichier /etc/fstab et en y ajoutant une ligne :

administrator@vm:~# sudo nano /etc/fstab
192.168.0.22:/volume1/Music /nas/music nfs auto,nofail,noatime,nolock,intr,tcp,actimeo=1800 0 0

Navidrome

On commence par créer un répertoire de travail ou sera installé le cache :

administrator@vm:~# sudo mkdir -pv /navidrome/data/

On installe le docker Navidrome avec la commande suivante : (voir les version Docker Compose à fin).

sudo docker run -d \
  --name navidrome \
  --restart=unless-stopped \
  --user $(id -u):$(id -g) \
  -v /nas/music:/music \
  -v /data/navidrome:/data \
  -p 4533:4533 \
  -e ND_LOGLEVEL=info \
  deluan/navidrome:latest

Il est possible de ne pas indiquer le user à des fin de test et debug, mais je ne le conseille pas de fonctionner en root. On retrouve ici nos deux répertoires, /data/navidrome pour le cache et /nas/music pour les fichiers musicaux qui pointe sur le NAS. Il y a pas mal d'autres options plus ou moins intéressantes que l'on pourra ajouter ici ou dans un fichier de configuration.

Il ne reste plus qu'à se connecter sur http://ip_serveur:4533 ... Il est bien sur possible de passer en SSL avec un reverse proxy (un petit Docker de plus...), mais également de créer des comptes secondaire pour d'autres utilisateurs qui pourront également télécharger fichiers ou albums.

Bonob

C'est ici que ça devient intéressant pour Sonos. Et ça se passe également sous Docker avec à minima :

sudo docker run -d \
  --name bonob \
  --restart=unless-stopped \
  -e BNB_PORT=4534 \
  -e BNB_URL=http://192.168.0.33:4534 \
  -e BNB_SONOS_SERVICE_NAME=Canaletto \
  -e BNB_SONOS_SEED_HOST=192.168.0.57 \
  -e BNB_SONOS_AUTO_REGISTER=true \
  -e BNB_SONOS_DEVICE_DISCOVERY=true \
  -e BNB_SUBSONIC_URL=http://172.17.0.2:4533 \
  -p 4534:4534 \
  simojenki/bonob

Il y a quelques options qui méritent explication :

  • BNB_URL= l'url du service Bonob afin de le faire savoir à Sonos
  • BNB_SONOS_SERVICE_NAME= Le nom du service qui apparaitra dans Sonos

  • BNB_SONOS_SEED_HOST= L'IP (fixe) d'un équipement Sonos permanent

  • BNB_SUBSONIC_URL= L'url interne à docker de Navidrome

Pour le reste je vous renvoi au GitHub afin d'adapter votre configuration.

A ce stade il suffit d'aller dans l'interface Sonos et d'ajouter le service que l'on viens de créer

Et de s'authentifier avec le compte précédemment créé dans Navidrome et de profiter de votre bibliothèque (MP3, FLAC, etc). Navidrome va indexer la bibliothèque et servir de cache. Je trouve les temps de recherche excellents au regard des 192 217 fichiers de ma bibliothèque répartis dans 15 753 répertoires. Il est possible dans Navidrome de créer des listes de lecture et des favoris que l'on retrouvera sous Sonos, par contre il n'est pas possible d'explorer l'arborescence des fichiers comme le permet Sonos dans son service de base.

Selon l'échantillonnage et le transcodage souhaité il faudra peut être modifier la configuration de Navi drome pour s'y adapter.

EDIT 26/08/2023

Suite à une mise à jour j'ai refait en Docker Compose :

version: "3"
services:
  navidrome:
    container_name: c-navidrome
    image: deluan/navidrome:latest
    # user: 1000:1000 # should be owner of volumes
    ports:
      - "4533:4533"
    restart: unless-stopped
    environment:
      # Optional: put your config options customization here. Examples:
      ND_SCANSCHEDULE: 1h
      ND_LOGLEVEL: info  
      ND_SESSIONTIMEOUT: 24h
      ND_BASEURL: ""
    volumes:
      - "/data/navidrome:/data"
      - "/nas/music:/music:ro"
  bonob:
    container_name: c-bonob
    image: simojenki/bonob:latest
    user: 1000:1000 # should be owner of volumes
    ports:
      - "4534:4534"
    restart: unless-stopped
    environment:
      BNB_PORT: 4534
      # ip address of your machine running bonob
      BNB_URL: http://192.168.10.33:4534  
      BNB_SECRET: password
      BNB_SONOS_AUTO_REGISTER: "true"
      BNB_SONOS_DEVICE_DISCOVERY: "true"
      BNB_SONOS_SERVICE_ID: 246
      BNB_SONOS_SERVICE_NAME: "Canaletto Music"
      # ip address of one of your sonos devices
      BNB_SONOS_SEED_HOST: 192.168.10.115
      BNB_SUBSONIC_URL: http://192.168.10.33:4533

EDIT 27/08/2023

On peut également choisi d'installer tout ça dans un VPS. Ca se complique un peu mais rien d'insurmontable. Par contre il faudra ensuite configurer les service à la main sur Sonos. La configuration comporte quelques différences, notamment au niveau de la reconnaissance automatique des équipements Sonos et de l'url externe. A noter que je fais pointer Bonob vers l'url Docker interne de Navidrome.

version: "3"
services:
  navidrome:
    container_name: c-navidrome
    image: deluan/navidrome:latest
    ports:
      - "4533:4533"
    restart: unless-stopped
    environment:
      ND_SCANSCHEDULE: 1h
      ND_LOGLEVEL: info  
      ND_SESSIONTIMEOUT: 24h
      ND_BASEURL: ""
      ND_SPOTIFY_ID: "c9d4gsdfghsfdgdfsfhsdfghdsfghsh1"
      ND_SPOTIFY_SECRET: "611sdfhgdfshshsghsfgh075f"
      ND_DEFAULTLANGUAGE: "fr"
    volumes:
      - "/data/navidrome:/data"
      - "/nfs/music:/music:ro"
  bonob:
    container_name: c-bonob
    image: simojenki/bonob:latest
    user: 1000:1000 # should be owner of volumes
    ports:
      - "4534:4534"
    restart: unless-stopped
    environment:
      BNB_PORT: 4534
      BNB_URL: https://bonob.domain.tls
      BNB_SECRET: wrtl
      BNB_SONOS_AUTO_REGISTER: "false"
      BNB_SONOS_DEVICE_DISCOVERY: "false"
      BNB_SONOS_SERVICE_ID: 246
      BNB_SONOS_SERVICE_NAME: "Online Music"
      BNB_SUBSONIC_URL: http://172.19.0.3:4533
      BNB_ICON_FOREGROUND_COLOR: "#1db954"
      BNB_ICON_BACKGROUND_COLOR: "#121212"
      TZ: "Europe/Paris"

Je sécurise le tout avec HAProxy sous pfsense (SSL). On teste les deux URL :

  • https://music.domain.tld : Ici on a l'interface de Navidrome et la première chose à faire est de créer un compte d'administration et de vérifier que tout fonctionne.
  • https://bonob.domain.tld : Ici on obtient l'interface de Bonob ou on va trouver les information pour configurer notre système Sonos.

Conseil : au niveau du firewall/reverse proxy vous pouvez appliquer rune restriction par IP afin de ne pas laisser tt ça ouvert aux quatre vents...

Une fois ceci configuré et fonctionnel, on passe à sonos en commençant par repérer l'adresse IP d'un des équipements et on lance :

http://192.168.210.115:1400/customsd.htm

Et là on tombe sur une archaïque interface ou l'on va saisir les informations recueillies sur la page https://bonob.domain.tld :

Il suffit ensuite s'ajoute le service dans l'interface Sonos (je l'ai fait sous Windows, mais c'est surement possible depuis un mobile) et de se connecter avec l'identifiant Navidrome pour voir apparaitre la bibliothèque Navidrome sous Sonos :

Vous trouvez ça trop compliqué, je comprends. Vous pouvez toujours vous abonner à un service de musique en ligne, c'est ce que je fais avec Spotify et Quobuz, mais je voulais également profiter de quelques morceaux introuvables que j'ai convertit moi même ! Une autre possibilité est de passer par un serveur Plex, mais l'interface est bien plus lente et je trouve pratique Navidrome pour construire des playlist...

EDIT 27/08/2023

J'ai essayé de faire passer Bonob via Cloudflared dans un container Docker. Tout se passe bien, sauf qu'il qu'au final il est impossible de lire sur Sonos. A suivre.

Freebox / Unifi UDM, DHCP & IPV6

De base l'UDM / UDM Pro en mode bridge sur une Freebox fonctionne en IPV4. Ca fait le job, mais parfois on peut avoir besoin d'un adressage en IPV6. J'ai lu pas mal de choses sur ce sujet, plus ou moins précises, notamment sur ce fil et sur ce site, j'ai eu quelques difficultés de mise en œuvre et je vais essayer de faire une synthèse simple.

La première chose à faire est de récupérer l'IPV6 du port WAN actif de l'UDM en s'y connectant en SSH avec la commande ip addr | grep "global dynamic" -B2 -A3 :

# ip addr | grep "global dynamic" -B2 -A3
3: eth9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc htb state UP group default qlen 10000
    link/ether 74:ac:b9:14:2d:e2 brd ff:ff:ff:ff:ff:ff
    inet 86.66.125.69/24 scope global dynamic eth9
       valid_lft 602510sec preferred_lft 602510sec
    inet6 fe80::76ec:b9xx:fe15:2df2/64 scope link
       valid_lft forever preferred_lft forever

L'IPV6 de notre port WAN est donc : fe80::76ec:b9xx:fe15:2df2/64

On va ensuite sur la page de configuration de la Freebox, dans mon cas une Delta S connectée à l'UDM avec un câble SFP+ dans l'espoir (vain) d'obtenir les 8 Gbit/s promis en 10G EPON. Donc sur http://mafreebox.freebox.fr (Paramètres de la Freebox / Configuration IPV6) et on reporte cette adresse dans le second Next Hop (et pas le premier hein !) afin de déléguer un préfixe. Et on note le préfixe associé.

On configure le port WAN de l'UDM en client DHCPv6 avec une taille de délégation de 64 :

Maintenant on va aller configurer le LAN de l'UDM. On reporte le préfixe précédemment noté au niveau IPV6 Gateway/Subnet et si on souhaite activer le serveur DHCP on défini une IP de départ et une IP de fin. Je ne l'ai pas fait car je souhaitait utiliser le DHCP de mon contrôleur de domaine Windows (pourquoi faire simple... mais idée abandonnée.). Pensez à cocher l'option RA.

A partir de là en SSH sur l'UDM on doit pouvoir pinguer en IPV6 :

# ping6 www.ibm.com
PING www.ibm.com (2a02:26f0:2b00:383::1e89): 56 data bytes
64 bytes from 2a02:26f0:2b00:383::1e89: seq=0 ttl=52 time=11.222 ms
64 bytes from 2a02:26f0:2b00:383::1e89: seq=1 ttl=52 time=11.307 ms
64 bytes from 2a02:26f0:2b00:383::1e89: seq=2 ttl=52 time=11.511 ms

Si on a pas configuré le serveur DHCP, on peut également rapidement configurer un client Windows en statique :

PS C:\Users\Lionel.SUPTEL> ping www.google.com -6
Pinging www.google.com [2a00:1450:4007:819::2004] with 32 bytes of data:
Reply from 2a00:1450:4007:819::2004: time=11ms
Reply from 2a00:1450:4007:819::2004: time=11ms
Reply from 2a00:1450:4007:819::2004: time=11ms
Reply from 2a00:1450:4007:819::2004: time=12ms

DHCP et résilience

Il me reste à configurer le serveur DHCP v6 sous Windows Serveur. Enfin, là se pose une vrai question sur les avantages et inconvénients. Actuellement j'ai un serveur Windows (VM dans un ESXi) dans une infrastructure Active Directory répartie sur plusieurs sites. Donc ça se justifie, et c'est même obligatoire au niveau du DNS.

Par contre, imaginons que demain je ne soit plus là ou dans l'incapacité de maintenir tout ça (infra IP et IoT). Il faut que l'accès internet soit résilient et puisse se passer du host ESXi . Mon idée est donc de basculer le DHCP sur l'UDM, de faire pointer les deux premiers DNS vers des serveurs AD et les deux suivants vers des DNS publics.

Mais pour cela il faut que le DHCP de l'UDM fasse aussi bien que celui de Windows. Et là ce n'est pas gagné. La lacune principale est qu'il n'y a rient pour facilement lister les baux. Sérieux ! Un jour surement dans une nouvelle interface encore plus design. A faire du beau ils en oublient souvent le fonctionnel chez Ubiquiti.

Par contre sur le DHCP WIndows j'utilise l'option 121 (Classless Static Route Option, RFC3442) qui se configure très facilement sur Windows. Sur l'UDM c'est un peu plus compliqué (mais pas plus que sur OpenSense ou Mikrotik). Heureusement il y a ici un calculateur qui va nous permettre de créer la chaine hexa que l'on va va pouvoir entrer dans les options personnalisées dans un champ texte des paramètres DHCP de l'UDM. Pourquoi faire simple !

Bien sur il est impossible d'importer les réservations DHCP de Windows vers l'UDM. Il va donc falloir se les faire à la main en cliquant sur chaque objet. Dans cette interface on peut également figer l'AP utilisé.

Il est intéressant de noter la possibilité d'affecter une IP réservée en dehors de la plage déclarée dans le DHCP. Et ça c'est intéressant. Dans un premier temps je crée un DHCP sur l'UDM avec uniquement une IP disponible et je laisse le DHCP Windows actif qui continuera à affecter les baux. Je fige les IP sur l'UDM et quand c'est terminé je peux arrêter le DHCP Windows et élargir la plage sur l'UDM, et accessoirement faire marche arrière au besoin.

Par contre coté IPv6 il n'est pas possible à ce jour de figer les IP. Ca viendra, peut-être...

Sources

 

Home Assistant & IP's

Suite à une configuration un peu courte de la plage de mon serveur DHCP, j'ai eu des modules Shelly (et d'autres) qui ont un peu perdu les pédales... En IT je monitore avec des outils IP comme PRTG, mais l'idée m'est venue de monitorer les modules domotique dans Home Assistant.

Pour ça il y a l'intégration ping, une intégration qui doit dater des débuts de Home Assistant et qui ne remonte pas l'IP dans les attributs. Et on verra plus loin que ce serait bien !

La première solution

On commence donc par créer un fichier ping.yaml dans nos packages et d'ajouter nos sensors :

binary_sensor:
  - platform: ping
    host: 192.168.210.63
    name: "Ping : Shelly Plug S01 | Sonos SdB"
    count: 3
    scan_interval: 30
  - platform: ping
    host: 192.168.210.78
    name: "Ping : Shelly Plug S02 | Sonos Terrasse"
    count: 3
    scan_interval: 30
  - platform: ping
    host: 192.168.210.104
    name: "Ping : Shelly Plug S03 | Congellateurs"
    count: 3
    scan_interval: 30

Ensuite on crée un groupe, et comme j'en ai beaucoup j'ai cherché quelque chose pour créer un groupe en mode willcard, genre binary_sensor.ping_* . Mais il n'existe pas grand chose et j'ai juste trouvé Groups 2.0 sous AppDaemon (mais vous n'allez pas l'installer juste pour ça !). Enfin voici le code à ajouter pour ceux qui sont habitués à la chose :

###########################################################################################
#                                                                                         #
#  Rene Tode ( [email protected] )                                                            #
#  2017/11/29 Germany                                                                     #
#                                                                                         #
#  wildcard groups                                                                        #
#                                                                                         #
#  arguments:                                                                             #
#  name: your_name                                                                        #
#  device_type: sensor # or any devicetype                                                #
#  entity_part: "any_part"                                                                #
#  entities: # list of entities                                                           #
#    - sensor.any_entity                                                                  #
#  hidden: False # or True                                                                #
#  view: True # or False                                                                  #
#  assumed_state: False # or True                                                         #
#  friendly_name: Your Friendly Name                                                      #
#  nested_view: True # or False                                                           #
#                                                                                         #
###########################################################################################

import appdaemon.plugins.hass.hassapi as hass
class create_group(hass.Hass):

  def initialize(self):
    all_entities = self.get_state(self.args["device_type"])
    entitylist = []
    for entity in all_entities:
      if self.args["entity_part"] in entity:
        entitylist.append(entity.lower())
    if "entities" in self.args:
      for entity in self.args["entities"]:
        entitylist.append(entity.lower())
    hidden = self.args["hidden"]
    view = self.args["view"]
    assumed_state = self.args["assumed_state"]
    friendly_name = self.args["friendly_name"]
    name = "group." + self.args["name"]    
    if not self.args["nested_view"]:
      self.set_state(name,state="on",attributes={"view": view,"hidden": hidden,"assumed_state": assumed_state,"friendly_name": friendly_name,"entity_id": entitylist})
    else:
      self.set_state(name + "2",state="on",attributes={"view": False,"hidden": hidden,"assumed_state": assumed_state,"friendly_name": friendly_name,"entity_id": entitylist})
      self.set_state(name,state="on",attributes={"view": True,"hidden": hidden,"assumed_state": assumed_state,"friendly_name": friendly_name,"entity_id": [name + "2"]})

Et ensuite dans apps.yaml pour créer le groupe :

pings_ip:
  module: groups
  class: create_group
  name: ping_ip
  device_type: binary_sensor
  entity_part: "ping_"
  entities:
    - sensor.group_ping
  hidden: False
  view: True
  assumed_state: False
  friendly_name: Ping IP4
  nested_view: False #creates a second group inside a viewed group  

A partir de là il est possible de créer une auto_entity_card pour visualiser nos sensors :

Quant au groupe il va nous servir à envoyer des messages afin de signaler les objets inactifs. Pour ça on va créer (Merci @mathieu...) un sensor :

    - platform: template
      sensors:
        offline_ping_sensors:
          friendly_name: 'Ping Offline'
          value_template: >
            {% set unavailable_count = states
                                    | selectattr('state','in', ['off', 'disconnected'])
                                    | selectattr('entity_id','in',state_attr('group.ping_ip','entity_id'))
                                    | map(attribute='entity_id')
                                    | list
                                    | length
            %}
            {{ unavailable_count }}
          attribute_templates:
            IP Fail: >
              {% set unavailable_list = states
                                | selectattr('state','in', ['off', 'disconnected'])
                                | selectattr('entity_id','in',state_attr('group.ping_ip','entity_id'))
                                | map(attribute='entity_id')
                                | list
                                | join('\n') 
                                | replace("binary_sensor.ping", "")
                                | replace("_", " ")
                                
              %}
              {{ unavailable_list }}

Et une automation qui sera déclenchée par ce sensor et va nous envoyer une notification :

- alias: 'IP'
  id: fc48c309-63c5-4965-bd87-fbcabc026983
  initial_state: true
  # mode: single
  trigger:
    - platform: state
      entity_id: sensor.offline_ping_sensors
  condition:
    - condition: template
      value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
  action:
    - delay: 00:01:00
    - service: notify.slack_hass_canaletto 
      data_template: 
        title: "IP Fail" 
        message: "{{now().strftime('%d/%m/%Y, %H:%M')}} > IP FAIL{{ state_attr('sensor.offline_ping_sensors','IP Fail') }} Count: {{ states('sensor.offline_ping_sensors') }}"

Le résultat est basic et se borne à nous envoyer la liste des modules injoignables, c'est intéressant mais ça ne mets pas en évidence un module qui tombe à un instant t :

De plus j'aimerait bien que les notifications soient cliquables afin d'ouvrir la page web des modules Shelly. J'ai donc remis @mathieu à contribution et apres une bonne nuit de sommeil on est partit sur une autre solution complémentaire.

Le plan B

On conserve nos sensors ping, mais on les renomme afin d'y intégrer le dernier digit de l'IP dans le nom, ce qui va nous donner. On est obligé d'écrire cette IP dans le nom car on ne la retrouve pas dans les attributs du sensor :

  - platform: ping
    host: 192.168.210.58
    name: "Ping 058 : Yeelink 01 | Desk"
    count: 3
    scan_interval: 30

On remarque que l'IP 58 devient 058 afin de conserver un alignement correct dans notre auto_entity_card et on va traiter ça dans l'automation ou vois apprécierait le template du message. Cette option nous impose de lister tous nos sensors dans l'automation, en attendant de trouver une façon de faire appel à un groupe ou le fichier des sensors :

- alias: 'Ping Offline'
  id: '08b1556d-d816-4879-b911-bd83213dd150'
  initial_state: true
  mode: single
  trigger:
    - platform: state
      entity_id:
        - binary_sensor.ping_127_shelly_plug_s09_udm
        - binary_sensor.ping_058_yeelight_01_desk
  condition:
    - condition: template
      value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
  action:
    - service: notify.slack_hass_canaletto 
      data_template: 
        title: "IP Fail" 
        message: '{{now().strftime("%d/%m/%Y, %H:%M")}} > {{ trigger.to_state.state|replace("on", "IP UP")|replace("off", "IP DOWN") }} : {{ trigger.to_state.attributes.friendly_name }} : {{ trigger.entity_id | replace("_0","",1) | replace("_1","1",1) | replace("_2","2",1) | replace("binary_sensor.ping", "http://192.168.210.") | replace("_"," ",1) | truncate(24, True,"") }}'

Au passage une astuce pour récupérer une liste d'entités, vous collez ça dans Outils de développement / Templates, ça vous évitera de vous coller la liste à la main :

{% for state in states %}
{{ state.entity_id }}
{%- endfor -%}

Et le résultat est maintenant uniquement sur le module défaillant, avec son url :

Il reste quelques petites choses d'ordre cosmétique, mais ça correspond maintenant à ce que j'attendais. J'envoie ces messages dans un fil Slack qui me sert de log dynamique que je regarde quand j'ai quelque chose en panne, en opposition aux notification par SMS (urgentes) ou via l'application.

Encore merci à Mathieu qui m'a bien aidé afin de mettre en code mes idées, et à la communauté afin d'améliorer tout ça !

Utiliser un tracker NMAP

Comme me l'a rappelé un utilisateur sur HACF Il y a une autre solution qui pourrait consister à utiliser l'intégration NMAP Tracker, qui, bien que pas faite pour présente l'avantage de remonter l'adresse IP en attribut. Pour faire propre il faudra renommer les trackers ainsi crées tant au niveau de l'entity_id que du friendly_name qui de base reprennent ce que retourne le DNS, qui n'est pas toujours très lisible.

Ca donne ça et il n'est pas utile de bricoler l'IP pour créer un lien car celle ci est disponible en attribut, ainsi que d'autres informations potentielement utiles.

- alias: 'IP Track Offline'
  id: '96017908-d46d-40cc-8d95-6b7997f5a411'
  initial_state: true
  mode: single
  trigger:
    - platform: state
      entity_id:
        - device_tracker.nmap_shelly_1_01
        - binary_sensor.numero_3
        - binary_sensor.numero_4
        - binary_sensor.numero_5
  condition:
    - condition: template
      value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
  action:
    - service: notify.slack_hass_canaletto 
      data_template: 
        title: "IP Fail" 
        message: '{{now().strftime("%d/%m/%Y, %H:%M")}} > {{ trigger.to_state.state|replace("home", "IP UP")|replace("not_home", "IP DOWN") }} : {{ trigger.to_state.attributes.friendly_name }} : http://{{ trigger.to_state.attributes.ip }}'

Pour ce résultat :

Alternatives

Il est bien sur également possible d'utiliser des outils de monitirig IP de l'IT. Au delà du monde de l'IT pro, j'en ai découvert un de très sympa, Uptime Kuma, et dont l'auteur travaille à une intégration avec Home Assistant. On y reviendra.

Pour surveiller des équipement ou sites sur internet il existe UpTime Robot qui dispose d'une intégration officielle dans HA qui remonte des binary_sensor. C'est très pratique car il y a tout ce qu'il faut pour créer des notification sur mesure.

Conclusion

Aucune de ces solutions n'est parfaite et je continue à chercher... Mais pour trouver une solution au problème de base, je pense qu'il vaudrait mieux surveiller la disponibilité des entités. En effet j'ai remarqué que parfois un Shelly peut être disponible en IP et injoignable en CoIoT. Cela permettrait également de surveiller des équipements non IP, par exemple Zigbee...

Toutes les idées sont bienvenues et avec plaisir pour échanger, ici dans les commentaires ou sur le forum HACF..

 

Yealink, Teams Phone

Cet article va surprendre ceux qui me connaissent car je n'ai jamais été un fan de Teams. Et pour deux raisons, d'une part Microsoft a profité de la pandémie pour lancer un produit à l'arrache, et si maintenant Teams est utilisable, ça n'a pas toujours été le cas. D'autre part Microsoft en offrant Teams sans surcout dans le cadre d'une offre groupée Microsoft 365, anéantit toute forme de concurrence possible. Ce n'est pas sain pour le marché et il serait temps que les autorités de régulation se penchent sur le cas Microsoft. Ce coup de gueule étant posé, il n'en reste pas moins que Teams est devenu une évidence quasi incontournable, au point qu'aujourd'hui il n'est pas rare d'entendre l'expression "On se fait un Teams" comme jadis "On se fait un Skype", ou "On se fait un Facetime" pour les paumés de Cupertino...

C'est quoi Teams ?

Je serait réducteur en disant que ce n'est jamais que la version moderne (et moins fun) de MSN Messenger (voire des BBS ou ICQ pour les plus anciens). Donc du tchat et de la visio en groupe ou en one to one agrémenté de plusieurs fonctionnalités autour des fichiers plus ou moins utiles et utilisées. Dans la pratique Teams c'est surtout des réunions de groupe en visio, et ça fonctionne maintenant très bien. C'est ici qu'ont échoué les technologies du pharamineux rachat de Skype. Quant à la concurrence qui rame face à ce rouleau compresseur, c'est Slack, Zoom ou encore Meet chez Google.

Si Teams s'utilise naturellement sur un ordinateur personnel ou un smartphone, ce qui va m'intéresser ici c'est de l'utiliser sur un poste d'entreprise. De base Teams sait faire de la téléphonie entre utilisateurs Teams et Microsoft vend a prix d'or des options qui permettent de communiquer avec le réseau public. Ces options ne sont pas viables en terme de tarif quand on sait que l'équivalent en SIP coutera moins de 5 € / mois, par exemple chez OVH. Et c'est ici qu'intervient les postes Yealink qui savent fonctionner en mode hybride et ainsi supporter un compte Teams et plusieurs lignes SIP. Et un poste d'entreprise permet également d'accéder à toutes ces technologies pour ceux qui sont habitués à tenir un combiné en main, qui a dit que les habitudes ont la vie dure ?

Grand écran

Ce poste dispose d'un écran confortable et les deux modes évoluent dans deux vases clos sans aucune interaction, si ce n'est de passer de l'un à l'autre. On aurait pu imaginer que le SIP profite des contacts qui remontent dans Teams, mais non, le SIP ne semble être présent que pour assurer ne transition en douceur vers la téléphonie Teams.

La partie Team est très simple à configurer, il suffit de saisir une URL sur son PC pour que la configuration se fasse automatiquement. Ensuite on dispose de toutes les fonctionnalités habituelles, mais en dehors de la téléphonie on se rabattra sur le PC car si partager un écran est possible sur ce téléphone, ça teste petit. Quant à la visio il faudra un modèle plus haut de gamme intégrant une caméra. Il y a bien un port USB, mais il ne servira qu'à connecter un micro / casque de la marque, pas une webcam...

Pauvreté des fonctionnalités SIP

La partie SIP est assez pauvre en fonctionnalités si ce n'est de gérer 16 lignes qui fonctionnent très bien avec par exemple des comptes OVH ou Keyyo. Et d'ailleurs l'écran SIP avec son look à la Windows XP semble issu du siècle dernier et contraste avec l'écran Teams.

Ici pas de bouton d'accès direct, pas d'interactions possibles via des URL comme cela est possible chez Fanvil ou il est possible d'activer / programmer le DND via une une URL, d'ailleurs ici je n'ai même pas trouvé la fonction DND... Et la seule solution que j'ai trouvée pour que ce poste ne sonne pas la nuit est de programmer via Home Assistant le down du port POE sur le switch... Il n'y a pas non plus la possibilité de personnaliser la sonnerie, et donc encore mois d'avoir des sonneries différentes en fonction des correspondants (dispo sur le moindre mobile Android), pas plus que d'avoir une sonnerie différentes en Teams ou SIP. Ce téléphone tourne sous Android, mais cet Android est inaccessible, ne comptez donc pas en profiter et on se contrefout donc qu'il tourne sous Android ou un OS propriétaire...

Conclusion

Ce poste, qui existe également en version Zoom ainsi que dans d'autres versions plus ou moins évoluées, pourra convenir dans certaines situation, notamment pour des managers à l'ancienne... C'est cher (250 €) mais tout en restant dans la gamme de prix des postes d'entreprise (allez donc voir la gamme de prix chez Cisco...).

Cela nous démontre également comment la téléphonie est en train d'évoluer, avant il y avait Alcatel et France Télécom, puis Cisco s'es imposé dans les grands groupes, exit Alcatel ou Matra, et demain ce sont les GAFAM's qui prendrons le pas sur les opérateurs, exit Orange...