đ Navigateur#
Nous avons dĂ©veloppĂ© une librairie Ă©crite en typescript qui permet dâĂ©crire des applications clientes dâun daemon Olvid dans une page web: @olvid/bot-web. Lâutilisation de ce module est cependant limitĂ© et amĂšne des contraintes ainsi que des enjeux en termes de sĂ©curitĂ©.
Limitations et contraintes#
Utilisation dâun proxy#
Actuellement une page web ne peut parler directement Ă un daemon gRPC. Il faut utiliser un proxy. Celui-ci sera mis en place en mĂȘme temps que notre daemon dans la procĂ©dure suivante.
Envoi de fichiers impossible#
Les outils que nous utilisons pour dĂ©velopper cette librairie ne permettent pas dâutiliser des mĂ©thodes de type client side streaming et bidirectional streaming de gRPC. Il en rĂ©sulte quâil nâest pas possible dâenvoyer des piĂšces jointes, ou de changer les photos de profil.
Sécurité#
Dans le cas oĂș votre page / application web doit ĂȘtre accessible sur Internet nous vous recommandons dâĂȘtre trĂšs prudent lors de lâexposition de votre daemon. Nous vous recommandons de mettre en place Ă minima une authentification HTTP, et si possible une authentification par certificat.
Faites également attention à ne pas stocker en clair votre clé client dans votre page web. Si votre proxy gRPC est authentifié (idéalement par certificat), vous pouvez ajouter la clé client directement au niveau du reverse-proxy, en ajoutant un header daemon-client-key.
Installation#
Daemon et proxy#
Voici un exemple dâinfrastructure docker compose pour dĂ©ployer un daemon et son proxy gRPC.
services:
daemon:
image: olvid/bot-daemon:2.0.0
environment:
- OLVID_ADMIN_CLIENT_KEY_CLI=SetARandomValue
volumes:
- ./data:/daemon/data
proxy:
image: olvid/grpc-web-proxy
ports:
- "8080:8080"
command: ["--backend_addr=daemon:50051", "--run_tls_server=false", "--server_http_max_read_timeout=0s", "--server_http_max_write_timeout=0s"]
restart: unless-stopped
cli:
image: olvid/bot-python-runner:2.0.0
entrypoint: "olvid-cli"
environment:
- OLVID_ADMIN_CLIENT_KEY=SetARandomValue
- OLVID_DAEMON_URL=http://daemon:50051
stdin_open: true
tty: true
profiles: ["cli"]
Vous pouvez ensuite lancer votre daemon et son proxy.
docker compose up -d daemon proxy
Pour lâinitialisation et la mise en place de ce daemon je vous invite Ă regarder notre section đ Quickstart.
Client#
Une fois votre daemon en place vous pouvez commencer à écrire votre page / application web. Il existe de nombreux moyens et framework pour faire cela, nous ne pourrons pas tous les détailler, mais nous allons voir les bases.
Pour notre exemple nous réaliserons une page web simple qui envoie un message dans toutes les discussions de votre bot.
Pour cela nous utiliserons npm pour lâinstallation du module, et le frameworkvite.
On commence par préparer notre projet et installer les dépendances.
npm install @olvid/bot-web
npm install vite
npm pkg set scripts.dev="npx vite"
On peut ensuite créer deux fichiers qui contiennent le code de notre page de démonstration.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script type="module" src="./index.js"></script>
<title>Olvid in a Web Page</title>
</head>
<body>
<input id="send-input" type="text"/>
<button id="send-button">Send</button>
</body>
</html>
index.js
import {OlvidClient} from "@olvid/bot-web";
const client = new OlvidClient({
serverUrl: "http://localhost:8080",
clientKey: "" // TODO set me !
});
let input = document.getElementById("send-input");
let button = document.getElementById("send-button");
// callback to send a message in every bot discussion
async function broadcastMessage(body) {
if (!body) {
console.error("Message body cannot be empty");
return
}
for await (let discussion of client.discussionList()) {
await client.messageSend({discussionId: discussion.id, body: body})
}
}
// send message when you click send or press enter in input
button.onclick = async () => { await broadcastMessage(input.value) }
input.addEventListener("keydown", async (e) => {
if (e.code === "Enter") {
await broadcastMessage(input.value)
}
});
client.onMessageSent({callback: message => {
console.log("> ", message.body)
}});
client.onMessageReceived({callback: message => {
console.log("< ", message.body);
}})
Pensez Ă remplacer la valeur de votre clĂ© client dans le fichier index.js au moment de lâinstanciation de la classe OlvidClient, et Ă modifier la valeur de serverUrl dans le cas oĂș celle par dĂ©faut ne conviendrait pas Ă votre infrastructure.
Pour tester votre programme il suffit de lancer la commande suivante et de vous rendre Ă lâadresse indiquĂ©e.
npm run dev
Reverse-proxy#
Une fois votre daemon en place et votre application web developpé, vous pourriez avoir besoin de la déployer en dehors de votre machine locale. Nous allons donc voir les différentes configurations possibles de nginx en reverse-proxy.
Configuration basique#
Voici un exemple de configuration basique sans authentification. Attention, nâexposez pas ce proxy sur un rĂ©seau en lâĂ©tat.
server {
listen 80;
server_name daemon-proxy.example.com
access_log /var/log/nginx/daemon-proxy-access.log;
error_log /var/log/nginx/daemon-proxy-error.log;
location / {
proxy_pass http://localhost:5174;
proxy_buffering off;
proxy_read_timeout 1d;
proxy_connect_timeout 1d;
proxy_send_timeout 1d;
}
}
Ajouter de lâauthentification#
HTTP#
Il faut tout dâabord crĂ©er un fichier de mot de passe. Vous pouvez suivre la documentation officielle de nginx.
Il suffit ensuite dâactiver lâauthentification HTTP pour notre reverse-proxy.
server {
listen 80;
server_name daemon-proxy.example.com
access_log /var/log/nginx/daemon-proxy-access.log;
error_log /var/log/nginx/daemon-proxy-error.log;
location / {
auth_basic "Daemon Proxy";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:5174;
proxy_buffering off;
proxy_read_timeout 1d;
proxy_connect_timeout 1d;
proxy_send_timeout 1d;
}
}
Certificats#
Nous vous recommandons de mettre plutÎt en place une authentification par certificat, pour cela vous pouvez vous réferrer à la documentation officielle nginx.
Clé Client#
Avertissement
Cette section ne doit ĂȘtre suivie que dans le cas oĂș lâaccĂšs Ă votre reverse-proxy est correctement authentifiĂ©
(cf Ajouter de l'authentification) !
Dans le cas contraire votre daemon pourrait devenir librement accessible sur internet.
Pour éviter de mettre la clé client dans votre page / application web vous pouvez la spécifier au niveau de votre reverse-proxy.
Pour cela vous pouvez ajouter la ligne suivante (en la modifiant) au bloc location de votre configuration nginx.
proxy_set_header daemon-client-key MY_CLIENT_KEY;
â ïž Cela permettra que nâimporte quel client, authentifiĂ© sur le proxy, accĂ©de au daemon avec la clĂ© client spĂ©cifiĂ© dans la configuration nginx.
Filtrage API#
Il est possible de nâexposer que les points dâentrĂ©e API nĂ©cessaires Ă votre application en faisant du filtrage au niveau du reverse-proxy.
Pour cela au lieu de spĂ©cifier un seul bloc location dans notre configuration nginx, on en ajoute un par point dâentrĂ©e Ă exposer, avec le mĂȘme contenu, mais des path diffĂ©rents.
Voici un exemple de configuration qui nâautorise que les commandes messageSend et discussionGet, et la notification onMessageReceived.
â ïž Cette configuration ne met pas en place dâauthentification, mais nous vous conseillons fortement de le faire cf Ajouter de l'authentification.
server {
listen 80;
server_name daemon-proxy.example.com
access_log /var/log/nginx/daemon-proxy-access.log;
error_log /var/log/nginx/daemon-proxy-error.log;
location /olvid.daemon.services.v1.MessageCommandservice/MessageSend {
proxy_pass http://localhost:5174;
proxy_buffering off;
proxy_read_timeout 1d;
proxy_connect_timeout 1d;
proxy_send_timeout 1d;
}
location /olvid.daemon.services.v1.DiscussionCommandservice/DiscussionGet {
proxy_pass http://localhost:5174;
proxy_buffering off;
proxy_read_timeout 1d;
proxy_connect_timeout 1d;
proxy_send_timeout 1d;
}
location /olvid.daemon.services.v1.MessageNotificationService/MessageReceived {
proxy_pass http://localhost:5174;
proxy_buffering off;
proxy_read_timeout 1d;
proxy_connect_timeout 1d;
proxy_send_timeout 1d;
}
}
Partager le domaine#
Pour des raisons de praticitĂ© ou pour Ă©viter des soucis de Cross-Origin vous pouvez ĂȘtre amenĂ© Ă hĂ©berger votre proxy daemon et votre page/application web sur le mĂȘme domaine. Voici un exemple de configuration nginx pour ce cas de figure.
Dans ce cas, pensez Ă changer lâURL de connection Ă votre daemon en ajoutant le suffixe /proxy dans votre application.
server {
listen 80;
server_name daemon-proxy.example.com
access_log /var/log/nginx/daemon-proxy-access.log;
error_log /var/log/nginx/daemon-proxy-error.log;
# proxy is only accessible with /proxy prefix, but we remove it before transfering requests to grpc-web-proxy
location /proxy/ {
rewrite /proxy/(.*) /$1 break;
proxy_pass http://localhost:5174;
proxy_buffering off;
proxy_read_timeout 1d;
proxy_connect_timeout 1d;
proxy_send_timeout 1d;
}
# configuration to your web page / application
location / {
[...]
}
}