रिवर्स प्रॉक्सी कॉन्फ़िगरेशन¶
Evey, API और वेब इंटरफ़ेस दोनों को एक साथ पोर्ट 9091 पर सादे HTTP में
देता है। रिवर्स प्रॉक्सी को बस TLS संभालना है और सब कुछ evey:9091 पर भेजना
है। नीचे अपना प्रॉक्सी चुनें (साथ आने वाला Caddy, Nginx, Traefik, Nginx Proxy
Manager, या वह जो आप पहले से चला रहे हैं)। अगर आप स्ट्रीमिंग को ट्यून करते हैं
या अपने सुरक्षा हेडर खुद सेट करते हैं, तो आखिर के दो सेक्शन भी पढ़ें।
Evey को प्रॉक्सी के बारे में बताएँ¶
प्रॉक्सी कोई भी हो, Evey को उसका सार्वजनिक पता चाहिए और यह भी कि किस प्रॉक्सी
पर भरोसा करना है। दोनों की जगह एडमिन → रिमोट एक्सेस है: https:// वाला
बाहरी URL, और जब प्रॉक्सी एक कंटेनर हो, तो विश्वसनीय प्रॉक्सी में
उसका नेटवर्क। दोनों बिना रीस्टार्ट के लागू होते हैं। https:// पते से ही Evey
को पता चलता है कि उसके आगे एक प्रॉक्सी TLS संभाल रहा है: कुकी को Secure
फ़्लैग मिलता है, HSTS भेजा जाता है, और विश्वसनीय प्रॉक्सी से आया
X-Forwarded-Proto माना जाता है। रिमोट एक्सेस का स्विच और क्लाइंट का असली
पता रिमोट एक्सेस और HTTPS में समझाए गए हैं।
यही जवाब environment से भी आ सकते हैं, compose फ़ाइल में environment: के
नीचे, उस इंस्टॉलेशन के लिए जो पूरा कोड के रूप में लिखा है (दोनों सेट हों तो
पैनल के मान को प्राथमिकता मिलती है):
services:
evey:
environment:
EVEY_SERVER_BASE_URL: https://media.example.com
EVEY_TRUSTED_PROXIES: 172.16.0.0/12 # सिर्फ़ तब ज़रूरी जब प्रॉक्सी एक कंटेनर हो; loopback पर हमेशा भरोसा है
पुराना EVEY_FORWARDED_PROTO अब भी ओवरराइड के रूप में माना जाता है, लेकिन
पुराना पड़ चुका है: पते का scheme ही काफ़ी है।
साथ आने वाले सभी उदाहरण डोमेन EVEY_DOMAIN से पढ़ते हैं, और जो फ़ाइल आप कॉपी
करते हैं उसमें एक placeholder होता है: उसे वहीं अपने डोमेन से बदलें, प्रॉक्सी
सर्विस (Caddy, Nginx) की EVEY_DOMAIN: लाइन में या Host(...) नियम (Traefik)
में।
Caddy (साथ आने वाला)¶
फ़ाइलें¶
docker/Caddyfile, जिसे
docker/Dockerfile.caddy एक इमेज में बनाता है;
रिपॉज़िटरी की docker-compose.yml में caddy
सर्विस।
सेट करें¶
caddyसर्विस कीEVEY_DOMAIN:लाइन में अपना डोमेन लिखें, और पक्का करें कि होस्ट के पोर्ट 80 और 443 Caddy कंटेनर तक पहुँचते हैं।- रिपॉज़िटरी की compose फ़ाइल से
caddyसर्विस अपनी फ़ाइल में जोड़ें औरdocker compose up -dचलाएँ। CaddyEVEY_DOMAINके लिए Let's Encrypt सर्टिफ़िकेट अपने-आप ले लेता है। - एडमिन → रिमोट एक्सेस में
https://वाला बाहरी URL सेट करें, और विश्वसनीय प्रॉक्सी में172.16.0.0/12।
यह पहले से क्या संभालता है¶
सब कुछ evey:9091 पर भेजता है, और स्ट्रीमिंग वाले पथ बिना बफ़रिंग और बिना
टाइमआउट के सीधे आगे जाते हैं।
सुरक्षा वाले सेक्शन
के सुरक्षा हेडर और मानक CSP खुद तय करता है, और ऐप का अपना CSP हटा देता है,
ताकि ब्राउज़र तक ठीक एक ही पहुँचे; जिन दो पेज की अपनी नीति है, वे उसे रखते
हैं।
Nginx¶
फ़ाइलें¶
examples/nginx/evey.conf.template
और एक तैयार compose overlay,
examples/nginx/docker-compose.nginx.yml।
सेट करें¶
- overlay की
EVEY_DOMAIN:लाइन में अपना डोमेन लिखें। nginx इमेज शुरू होते समय template मेंEVEY_DOMAINभरकर उसे तैयार करती है। - सर्टिफ़िकेट और कुंजी वहीं रखें जहाँ template उन्हें ढूँढता है, उसी नाम से
(
nginx-certsvolume में/etc/ssl/certs/<domain>.pem,/etc/ssl/private/<domain>.key): overlay आपके लिए सर्टिफ़िकेट नहीं लेता। docker compose -f docker-compose.yml -f examples/nginx/docker-compose.nginx.yml up -d।- एडमिन → रिमोट एक्सेस में
https://वाला बाहरी URL सेट करें, और विश्वसनीय प्रॉक्सी में172.16.0.0/12।
यह पहले से क्या संभालता है¶
evey:9091 पर भेजता है: स्ट्रीमिंग वाले पथों पर बफ़रिंग और टाइमआउट बंद,
SyncPlay का WebSocket अपग्रेड होता है, X-Forwarded-For और
X-Forwarded-Proto आगे भेजे जाते हैं, और प्लगइन अपलोड और बैकअप रिस्टोर के
लिए client_max_body_size 2G। यह ऐप के अपने सुरक्षा हेडर और CSP को जैसे का
तैसा आगे जाने देता है।
Traefik¶
फ़ाइलें¶
examples/traefik/docker-compose.traefik.yml।
सेट करें¶
- Traefik को
websecureentrypoint,letsencryptसर्टिफ़िकेट resolver औरproxyनाम के एक बाहरी Docker नेटवर्क के साथ चलाएँ; overlay के labels यही नाम मानकर चलते हैं। -
overlay के
Host(...)नियम में अपना डोमेन लिखें, फिर: -
एडमिन → रिमोट एक्सेस में
https://वाला बाहरी URL सेट करें, और विश्वसनीय प्रॉक्सी मेंproxyनेटवर्क की रेंज (172.16.0.0/12Docker के डिफ़ॉल्ट नेटवर्क को ढक लेता है)।
यह पहले से क्या संभालता है¶
overlay सिर्फ़ router के labels जोड़ता है और proxy नेटवर्क से जुड़ता है।
डिफ़ॉल्ट सेटिंग पर Traefik न जवाबों को बफ़र करता है, न किसी जवाब की अवधि पर
सीमा लगाता है, इसलिए स्ट्रीमिंग के लिए कुछ ट्यून नहीं करना पड़ता। यह
X-Forwarded-For और X-Forwarded-Proto अपने-आप आगे भेजता है, और ऐप के सुरक्षा
हेडर को जैसे का तैसा जाने देता है।
Nginx Proxy Manager¶
फ़ाइलें¶
कोई नहीं: सब कुछ NPM के इंटरफ़ेस में सेट होता है। evey कंटेनर उसी नेटवर्क पर होना चाहिए जिसे NPM इस्तेमाल करता है, देखें वह प्रॉक्सी जो आप पहले से चला रहे हैं।
सेट करें¶
Edit Proxy Host में:
- Details: Scheme
http, Forward Hostnameevey-server, Forward Port9091। Websockets Support: on (SyncPlay)। Cache Assets: off - नहीं तो यह मीडिया के segments और playlists को एक तय मियाद के साथ कैश कर लेगा। Block Common Exploits चालू रह सकता है (अगर कभी कोई खोज या प्लगइन अपलोड बिना वजह 403 दे, तो इसे बंद करें)। - SSL: आपका सर्टिफ़िकेट, Force SSL, HTTP/2। HSTS यहाँ ज़रूरी नहीं है - ऐप उसे पहले से भेजता है।
- एडमिन → रिमोट एक्सेस में
https://वाला बाहरी URL सेट करें, और विश्वसनीय प्रॉक्सी में NPM नेटवर्क की रेंज।
यह पहले से क्या संभालता है¶
NPM X-Forwarded-For, X-Forwarded-Proto और Host हेडर अपने-आप आगे भेजता है,
और उसके डिफ़ॉल्ट टाइमआउट Evey की स्ट्रीम पर कभी नहीं लगते। और कुछ ज़रूरी नहीं
है।
ज़रूरी नहीं - NPM को direct play वाली फ़ाइलें अपनी डिस्क से बफ़र करने से
रोकें: Advanced → Custom Nginx Configuration (Custom Locations टैब
regex नहीं लिख सकता):
# प्लगइन अपलोड (100 MB तक) और बैकअप रिस्टोर (2 GB तक)।
client_max_body_size 2G;
# स्ट्रीमिंग - न बफ़रिंग, न टाइमआउट।
location ~ ^/api/v1/media/.+/(hls|stream) {
proxy_pass http://evey-server:9091;
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 0;
proxy_send_timeout 0;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# लाइब्रेरी स्कैन जब तक चलता है, अपनी प्रगति भेजता रहता है।
location ~ ^/api/v1/libraries/.+/scan {
proxy_pass http://evey-server:9091;
proxy_buffering off;
proxy_read_timeout 0;
proxy_send_timeout 0;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
बाकी हर पथ (वेब इंटरफ़ेस, बाकी API, SyncPlay का WebSocket) NPM के अपने प्रॉक्सी ब्लॉक से जाता है, जिसे Details टैब सेट करता है। यह snippet छोड़ दें तो भी कुछ नहीं टूटता - इससे सिर्फ़ यह बदलता है कि बड़ी फ़ाइलें कहाँ बफ़र होती हैं।
वह प्रॉक्सी जो आप पहले से चला रहे हैं (TLS पहले ही संभाला गया)¶
जिस NAS पर पहले से एक रिवर्स प्रॉक्सी है (NAS निर्माता का अपना, Nginx Proxy Manager, या किसी साझा Docker नेटवर्क पर Traefik), वहाँ Caddy की ज़रूरत नहीं है: Evey अपने सुरक्षा हेडर और पूरा CSP खुद भेजता है, और स्ट्रीमिंग के नियम वैसे भी सिर्फ़ वही प्रॉक्सी लागू कर सकता है जो क्लाइंट के सामने है। उसके पीछे दूसरा प्रॉक्सी कुछ नहीं जोड़ता।
सेट करें¶
evey सर्विस को उस प्रॉक्सी के नेटवर्क पर रखें और evey-server:9091 को
लक्ष्य के रूप में सेट करें (कंटेनर का नाम; सर्विस का नाम evey भी काम करता है):
services:
evey:
# कोई पोर्ट नहीं: अंदर आने का एकमात्र रास्ता प्रॉक्सी है
environment:
EVEY_SERVER_BASE_URL: https://media.example.com
EVEY_TRUSTED_PROXIES: 172.16.0.0/12 # प्रॉक्सी का subnet पता चलते ही इसे छोटा करें
# ...
networks:
default:
name: proxy-net
external: true
फिर उस प्रॉक्सी में, evey-server:9091 वाले route के लिए: X-Forwarded-For
और X-Forwarded-Proto आगे भेजें, WebSocket सपोर्ट चालू करें, और स्ट्रीमिंग
वाले पथों पर नीचे दिए स्ट्रीमिंग नियम लागू करें - ऊपर Nginx और Nginx Proxy
Manager वाले सेक्शन ठीक-ठीक लाइनें दिखाते हैं।
यह पहले से क्या संभालता है¶
यह प्रॉक्सी पर निर्भर है: जाँचें कि ब्राउज़र तक ठीक एक ही CSP हेडर पहुँचता है
(अगले सेक्शन देखें), और विश्वसनीय प्रॉक्सी सेट होने के बाद
evey admin doctor चलाएँ।
स्ट्रीमिंग - प्रॉक्सी की डिफ़ॉल्ट सेटिंग इस पर क्या असर डालती है¶
Evey अपनी डिफ़ॉल्ट सेटिंग पर छोड़े गए रिवर्स प्रॉक्सी के पीछे काम करता है: हर लंबा चलने वाला जवाब या तो लगातार बाइट भेजता है (HLS segments, direct play, ट्रांसकोड का आउटपुट), या कनेक्शन को जीवित रखता है (event streams keep-alive भेजते हैं), इसलिए डिफ़ॉल्ट read timeout कभी नहीं लगता। फिर भी दो बातें जानना अच्छा है:
- बफ़रिंग। जो प्रॉक्सी जवाबों को बफ़र करता है (nginx डिफ़ॉल्ट रूप से
करता है), वह direct play वाली फ़ाइल को क्लाइंट तक पहुँचने से पहले अपने बफ़र
और अस्थायी फ़ाइलों में पढ़ता है - यह काम करता है, लेकिन तब कई GB की फ़िल्म
प्रॉक्सी वाले होस्ट की डिस्क से होकर गुज़रती है। स्ट्रीमिंग वाले पथों पर
proxy_buffering offइससे बचाता है। - स्कैन।
/api/v1/libraries/*/scanस्कैन चलने तक प्रगति भेजता रहता है; जो प्रॉक्सी किसी जवाब की कुल अवधि पर सीमा लगाता है (ऐसा कम होता है - nginx, Caddy और Traefik डिफ़ॉल्ट रूप से नहीं लगाते), वह इसे बीच में काट देगा। स्कैन खुद सर्वर पर फिर भी चलता रहता है।
जिन पथों की बात है:
साथ आने वाला Caddy और Nginx का उदाहरण इन पथों पर दोनों सेट करते हैं (न बफ़रिंग, न टाइमआउट)। Traefik का उदाहरण और Nginx Proxy Manager जैसा इंटरफ़ेस से चलने वाला प्रॉक्सी अपनी डिफ़ॉल्ट सेटिंग रखते हैं, जो ठीक है; ऊपर वाला NPM snippet ज़रूरी नहीं है।
सुरक्षा हेडर और CSP - अगर आपका प्रॉक्सी अपने हेडर लगाता है तो यह पढ़ें¶
Evey एक पूरी Content-Security-Policy भेजता है। अगर आपका प्रॉक्सी उसे आगे जाने देता है, तो आपका काम हो गया।
कई सेटअप इसकी जगह इसे हटा देते हैं (Caddy में header_down
-Content-Security-Policy) और अपनी नीति खुद बनाते हैं - यह ठीक है, लेकिन तब
आपकी नीति में ये टोकन रहने चाहिए, नहीं तो कुछ सुविधाएँ चुपचाप टूट जाती
हैं:
| टोकन | Directive | इसके बिना क्या टूटता है |
|---|---|---|
'self' |
frame-src |
हर प्लगइन का इंटरफ़ेस और थीम स्क्रिप्ट (खाली iframe)। ये /plugin-frame.html में चलते हैं, जो Evey का एक पेज है जिसे ऐप एक frame में दिखाता है (नीचे देखें)। |
'wasm-unsafe-eval' |
script-src |
ब्राउज़र में AC-3/E-AC-3 प्लेबैक (ac3go का WebAssembly डिकोडर compile नहीं हो पाता, इसलिए फ़िल्म बिना आवाज़ के चलती है)। यह सीमित टोकन सिर्फ़ Wasm compile करने की अनुमति देता है, आम eval() की नहीं। |
https://www.gstatic.com |
script-src (+ frame-src) |
Google Cast का sender SDK कभी लोड नहीं होता, इसलिए किसी को कास्ट बटन नहीं मिलता। ब्राउज़र के लिए उपयोगकर्ता के अपने LAN पर Chromecast ढूँढने का यही एकमात्र तरीका है। |
blob: |
media-src |
MSE प्लेबैक (hls.js स्ट्रीम को blob URL से जोड़ता है)। |
blob: |
worker-src |
hls.js चुपचाप main thread पर demuxing करने लगता है (मोबाइल पर ज़्यादा अटकता है)। |
अपनी नीति बनाने वाले प्रॉक्सी के लिए एक जाँची-परखी नीति, वही जो साथ आने वाला Caddy भेजता है:
default-src 'self'; base-uri 'self'; object-src 'none';
img-src 'self' https: data: blob:; media-src 'self' blob:;
style-src 'self' 'unsafe-inline';
script-src 'self' 'wasm-unsafe-eval' https://www.gstatic.com;
connect-src 'self' https: wss:; worker-src 'self' blob:; font-src 'self' data:;
frame-src 'self' https://www.gstatic.com
base-uri और object-src के न होने पर default-src उनकी जगह नहीं लेता, इसलिए
जो नीति इन्हें छोड़ देती है, उसमें <base href> injection और
<object>/<embed> embedding पर कोई रोक नहीं रहती। अगर आप अपनी नीति खुद बनाते
हैं तो इन्हें रखें। script-src में 'unsafe-inline' नहीं है: पेज में डाली
गई स्क्रिप्ट नहीं चलती। इसे वापस न जोड़ें - ऐप में किसी चीज़ को इसकी ज़रूरत
नहीं है।
दो पेज की अपनी नीति है, जिसे ऐप भेजता है; इन दो पथों पर ऐप का हेडर रहने दें:
/plugin-frame.html, वह पेज जिसमें प्लगइन के इंटरफ़ेस और थीम स्क्रिप्ट चलते हैं। इसकी नीति इसे sandbox में रखती है (iframe के बाहर खोलने पर भी), इसके नेटवर्क को उन्हीं होस्ट तक सीमित करती है जो प्लगइन का manifest बताता है, और सिर्फ़ ऐप को इसे frame में दिखाने देती है, इसलिए यहX-Frame-Options: SAMEORIGINभी भेजता है: इस पथ पर इसेDENYसे न बदलें, नहीं तो हर प्लगइन का इंटरफ़ेस खाली रहेगा।/cast-receiver.html, वह पेज जो Chromecast Evey से चलाते समय लोड करता है। Cast प्लेयर इसमेंwww.gstatic.comसे styles, fonts और images डालता है। उस पथ पर ऐप का हेडर रहने दें, या यह नीति सिर्फ़ उसी पथ पर लगाएँ:
default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://www.gstatic.com; style-src 'self' 'unsafe-inline' https://www.gstatic.com; img-src 'self' data: https: blob:; media-src 'self' blob: https:; worker-src 'self' blob:; font-src 'self' data: https://www.gstatic.com; connect-src 'self' https: wss:; frame-src https://www.gstatic.com; base-uri 'self'; object-src 'none'
ब्राउज़र तक ठीक एक ही CSP हेडर पहुँचना चाहिए
दो हों (ऐप का और आपके प्रॉक्सी का, या Caddy का और Nginx Proxy Manager के
"Advanced" snippet का), तो ब्राउज़र दोनों का साझा हिस्सा लागू करते हैं:
सबसे सख्त नियम लागू होता है और गड़बड़ी का कोई संकेत नहीं मिलता। इससे जाँचें:
curl -sI https://your.domain | grep -ci content-security-policy, जिसे
1 छापना चाहिए।
किसी भी बदलाव के बाद दो और जाँचें:
- नीति को एक ही लाइन में रखें। प्रॉक्सी की config में कई लाइनों में बँटा
CSP चुपचाप बिगड़ने का जाना-माना मामला है: जोड़ते समय सिरे वाले अक्षर गायब हो
जाते हैं (एक बार
...gstatic.com; connect-srcबदलकर...gstaticonnect-srcहो गया, जिससे Cast SDK बंद हो गया और दो directives आपस में मिल गए)। - प्लगइन frame अपनी नीति रखता है:
curl -sI https://your.domain/plugin-frame.html | grep -i -e content-security -e x-frameमेंsandbox allow-scriptsऔरSAMEORIGINदिखने चाहिए।
क्लाइंट के असली IP और रिमोट एक्सेस की जाँच¶
जब प्रॉक्सी क्लाइंट का असली पता आगे भेजता है, तो Evey घर के दर्शक और इंटरनेट के दर्शक में फ़र्क कर पाता है: अधिकतम रिमोट बिटरेट और रिमोट एक्सेस का स्विच इसी पर काम करते हैं। दो नियम:
- विश्वसनीय प्रॉक्सी (पैनल या
EVEY_TRUSTED_PROXIES) में प्रॉक्सी शामिल होना चाहिए: Docker नेटवर्क पर कंटेनर हो तो172.16.0.0/12; होस्ट पर चलता हो तो कुछ जोड़ने की ज़रूरत नहीं, क्योंकि loopback पर हमेशा भरोसा है और आप जो भी सेट करते हैं वह उसमें जुड़ जाता है। - एक के पीछे एक लगे हर प्रॉक्सी को
X-Forwarded-Forबचाकर रखना होता है। Caddy में यहservers { trusted_proxies static private_ranges }है; nginxproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;इस्तेमाल करता है।
इसे चालू करने से बदल जाता है कि कौन रिमोट गिना जाता है: जिस सर्वर का रिमोट
एक्सेस बंद था, वह असली रिमोट क्लाइंट को मना करने लगता है
(403 {"code":"remote_access_disabled"}), जबकि ऐप की कोई सेटिंग नहीं बदली।
जब सार्वजनिक पता सेट हो और कोई चुनाव न किया गया हो, तो Evey रिमोट एक्सेस
अपने-आप खोल देता है, हर बार शुरू होने पर उसकी स्थिति और हर इनकार को क्लाइंट के
पते के साथ लॉग में लिखता है, और evey admin doctor "बाहर खुला है लेकिन मना कर
रहा है" पर विफल होता है। पूरा तरीका
रिमोट एक्सेस और HTTPS
में बताया गया है।