# Vhost du conteneur « li » — un nginx DEDIE, qui ne fait que verifier des # signatures et servir des fichiers. Il est derriere une facade (voir # facade.conf.example) qui porte le TLS. # # Chemins vus DANS le conteneur (voir compose.yaml) : # /etc/nginx/li/users.conf la map des ayants droit, engendree par li-user # /srv/li/library/ la bibliotheque, en lecture seule # /srv/li/playlists/ les .xspf et .html engendres par li-gen # La table des ayants droit : « map $arg_u $li_secret ». Engendree par # li-user, jamais editee a la main. # # Son « default "" » est le coeur du dispositif : un identifiant inconnu ou # revoque n obtient pas un secret par defaut devinable, il n obtient RIEN, et # la requete est refusee avant meme le calcul de signature. include /etc/nginx/li/users.conf; # L ADRESSE REELLE DU CLIENT, et non celle du proxy de facade. # # Sans ceci, $remote_addr vaut l adresse de la facade sur le reseau Docker : # toutes les lectures, tous les 403 et toute la limitation de debit seraient # imputes a un seul et meme voisin. # # La confiance porte sur les plages privees et non sur le sous-reseau du # jour : il est ATTRIBUE par Docker, et un reseau recree peut en changer. Une # plage figee ferait alors silencieusement reapparaitre l adresse du proxy # dans le journal — exactement le genre de panne qui ne se voit pas. Le risque # est nul tant que le reseau est « internal » et que la facade y est le seul # voisin de ce conteneur. # # X-Real-IP n est pas falsifiable : la facade l ECRASE a chaque requete avec # le $remote_addr qu elle constate, et c est elle qui est en frontal. set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.16.0.0/12; set_real_ip_from 192.168.0.0/16; real_ip_header X-Real-IP; # Une zone par ayant droit, et non par adresse : trois flux simultanes depuis # trois pays, c est du partage de lien, et cela se voit. limit_conn_zone $arg_u zone=li_ayants:1m; # Le journal d ACCES ne contient jamais $request_uri. La signature EST le # laissez-passer : un journal qui la porte est un journal rejouable, et il est # lu par plus de monde que la table des secrets. $uri est le chemin decode, # sans les arguments. nginx echappe lui-meme les caracteres speciaux de $arg_u. # # Le journal d ERREUR, lui, porte la ligne de requete entiere — donc la # signature — et nginx ne sait pas l en priver. Le cas est etroit : une # signature VALIDE dont le fichier a disparu. Il sort sur stderr, donc dans # les journaux Docker, lisibles du seul root de l hote. log_format li '$remote_addr "$arg_u" "$uri" $status $body_bytes_sent $request_time'; server { listen 80; server_name _; root /srv/li; autoindex off; access_log /dev/stdout li; # « hash,expiration » lus dans les arguments de l URL. La signature porte # sur $uri, c est-a-dire le chemin DECODE : le generateur signe donc le # chemin brut et n encode qu au moment d ecrire l URL. Une signature # calculee sur la forme encodee donne un 403 sur tout titre accentue. secure_link $arg_s,$arg_e; secure_link_md5 "$secure_link_expires$uri$li_secret"; # Ces trois controles sont au niveau server et non dans un location : la # phase de reecriture s execute AVANT le choix du location, ils couvrent # donc les deux arborescences sans etre ecrits deux fois. « if » suivi de # « return » est la seule forme que la documentation nginx ne deconseille # pas. if ($li_secret = "") { return 403; } # inconnu, ou revoque if ($secure_link = "") { return 403; } # signature fausse ou absente if ($secure_link = "0") { return 410; } # lien perime limit_conn li_ayants 3; limit_rate_after 16m; # les premiers 16 Mo a pleine vitesse : demarrage limit_rate 12m; # et avance rapide restent francs sendfile_max_chunk 512k; add_header Cache-Control "private, no-store" always; add_header X-Content-Type-Options "nosniff" always; location /library/ { } # Le sous-arbre « software » se TELECHARGE, il ne s affiche pas. # # On y depose du .txt, du .html, du .svg — que le navigateur rendrait # volontiers DANS la page, sur l origine de la bibliotheque. Ce serveur ne # pose aucun cookie et n a pas de session a voler. Mais un depot de # fichiers qui execute ce qu on y met est une surprise qu on s epargne # pour trois lignes. # # Ce chemin doit rester d accord avec LI_HTML_ONLY (li.env) : un # repertoire de plus la-bas, c est un location de plus ici. location /library/software/ { # « add_header » dans un location REMPLACE ceux du bloc parent au lieu # de s y ajouter. Les deux en-tetes du niveau server sont donc repris # ici : sans cela, ce sous-arbre perdrait son « no-store ». add_header Cache-Control "private, no-store" always; add_header X-Content-Type-Options "nosniff" always; add_header Content-Disposition "attachment" always; } # Ce repertoire contient DEUX documents par ayant droit : sa playlist # .xspf et sa page .html. Ils portent exactement les memes URL # signees — la signature ne depend que du chemin et du secret, jamais du # document qui la transporte. # # Et ils se typent tout seuls, chacun par une voie differente : # .html — connu de mime.types, donc servi en text/html sans rien faire ; # .xspf — inconnu de mime.types, donc rattrape par le default_type. # # D ou « default_type » et NON un bloc « types » : « types » REMPLACE la # table heritee de mime.types au lieu de s y ajouter. Un bloc « types » ici # ferait perdre au .html son type — et aux .mkv le leur. location /playlists/ { default_type application/xspf+xml; } location / { return 404; } }