Gunicorn: skema pendeteksian=='https' rusak jika terikat ke soket domain unix

Dibuat pada 30 Apr 2018  ·  57Komentar  ·  Sumber: benoitc/gunicorn

commit b07532be752668be5eb5dbd0a8303abf5c219c99 "Larang header skema aman yang kontradiktif" merusak "request.scheme" dan "wsgi.url_scheme" ketika gunicorn terikat ke soket domain unix. Ketika gunicorn terikat ke soket tcp semuanya berfungsi dengan baik.

Saat digunakan dengan nginx di depan gunicorn 19.8, nginx melewati HTTP_X_FORWARDED_PROTO == https , tetapi di gunicorn/http/message.py sejalan remote_addr = self.unreader.sock.getpeername() dalam kasus domain unix socket remote_addr adalah string kosong dan kemudian secure_scheme_headers adalah dict kosong dan skema akhirnya menjadi "http" meskipun server web hanya https dan melewati semua header.

Saya menggunakan pekerja gevent dengan semua yang terbaru di python 3.6 tapi saya rasa itu tidak penting.

Komentar yang paling membantu

Hai

Seperti yang dijanjikan, saya dengan cepat menerapkan pada server uji aplikasi Django yang sangat sederhana untuk mendemonstrasikan masalah tersebut masih ada.

Anda dapat menampilkannya dari url ini: http://gunicorn-test.exige.info dan https://gunicorn-test.exige.info
Sumber tersedia di https://github.com/alorence/gunicorn-test

Harap diperhatikan pada sebagian besar browser, ketika Anda pertama kali mengunjungi tautan https://, tidak mungkin untuk kembali ke http://. Mencapai halaman dengan curl bekerja dengan baik.

Seperti yang Anda lihat, dengan gunicorn 19.8.1, dengan soket unix yang digunakan untuk menyediakan koneksi antara proxy terbalik nginx dan instance gunicorn, wsgi.url_scheme tidak disetel dengan benar.

Saya baik-baik saja untuk memberikan informasi lebih lanjut jika Anda memberi tahu saya apa yang Anda butuhkan;)

Semua 57 komentar

@zt-initech terima kasih atas laporannya. Saya akan segera melihat.

@zt-initech #1767

Saya tidak yakin siapa lagi yang akan meninjaunya, tetapi ini adalah tambalan yang cukup sederhana dan saya mengujinya secara lokal. Jika Anda dapat memverifikasinya, saya akan menggabungkan dan membuat rilis.

@tilgovi tambalan Anda memperbaiki masalah untuk saya. Tapi saya akan melakukan ini sebagai gantinya:

            if self.unreader.sock.family == socket.AF_UNIX:
                secure_scheme_headers = cfg.secure_scheme_headers
            else:
                remote_addr = self.unreader.sock.getpeername()
                if isinstance(remote_addr, tuple):
                    remote_host = remote_addr[0]
                    if remote_host in cfg.forwarded_allow_ips:
                        secure_scheme_headers = cfg.secure_scheme_headers

dan saya bukan ahli dalam jaringan jadi saya tidak tahu apakah mengandalkan getpeername() mengembalikan string tidak masalah.

19.8.1 dirilis

@zt-initech Ah, ya. Setidaknya apa yang saya lakukan konsisten dengan apa yang kami lakukan di masa lalu, tetapi saya pikir Anda benar itu akan lebih baik.

Akan lebih jelas dengan AF_UNIX , tetapi kita dapat mengandalkan tipe string:

Alamat soket AF_UNIX yang terikat ke node sistem file direpresentasikan sebagai string

Sumber: https://docs.python.org/3/library/socket.html

Saya memiliki nginx di depan gunicorn, dan 19.8.1 masih menghasilkan kesalahan "Contradictory scheme headers" meskipun saya telah menetapkan proxy_set_header X-Forwarded-Proto $scheme; di nginx conf.

@danielskun apakah ada proxy terbalik lain (seperti penyeimbang beban) di depan nginx yang mengatur header yang berbeda?

Jika Anda dapat membuat repo kecil yang mereproduksi masalah, saya dapat melihatnya.

Hai,

Seperti @danielskun saya jatuh dalam kasus itu kemarin. Setelah penerapan standar aplikasi Django saya, saya melihat loop redirect 301 tak terbatas saat menghubungkan ke versi https-nya.

Ini disebabkan oleh opsi keamanan SECURE_SSL_REDIRECT , dan penyebabnya adalah pembaruan gunicorn dari 19.7.1 menjadi 19.8.1

Dengan 19.7.1, dengan SECURE_SSL_REDIRECT=True , Django mendeteksi dengan benar situs web dikunjungi dengan skema https. Lingkungan wsgi.url_scheme telah disetel dengan benar. Sekarang, dengan gunicorn 19.8.1, wsgi.url_scheme berisi http .

Aplikasi wsgi saya berjalan di belakang proxy terbalik nginx (tidak ada aplikasi depan lain, tidak ada penyeimbang beban) dan terhubung melalui soket unix (pada disk).

Saya tidak tahu cara men-debug ini, karena saya mengembangkan di windows, saya biasanya tidak dapat mensimulasikan koneksi https di mesin dev saya. Tetapi saya pikir saya dapat mengatur aplikasi mini wsgi/Django di server saya untuk dengan mudah mendemonstrasikan masalah tersebut. Dengan kode sumber terbuka, Anda dapat melihat sendiri. Tentu saja, saya siap membantu Anda untuk men-debug ini, saya hanya belum tahu bagaimana melanjutkannya ;)

Saya mungkin akan menerbitkan aplikasi mini ini besok, beri tahu saya jika Anda memerlukan info lebih lanjut. Semoga harimu menyenangkan

Hai

Seperti yang dijanjikan, saya dengan cepat menerapkan pada server uji aplikasi Django yang sangat sederhana untuk mendemonstrasikan masalah tersebut masih ada.

Anda dapat menampilkannya dari url ini: http://gunicorn-test.exige.info dan https://gunicorn-test.exige.info
Sumber tersedia di https://github.com/alorence/gunicorn-test

Harap diperhatikan pada sebagian besar browser, ketika Anda pertama kali mengunjungi tautan https://, tidak mungkin untuk kembali ke http://. Mencapai halaman dengan curl bekerja dengan baik.

Seperti yang Anda lihat, dengan gunicorn 19.8.1, dengan soket unix yang digunakan untuk menyediakan koneksi antara proxy terbalik nginx dan instance gunicorn, wsgi.url_scheme tidak disetel dengan benar.

Saya baik-baik saja untuk memberikan informasi lebih lanjut jika Anda memberi tahu saya apa yang Anda butuhkan;)

Terima kasih @alorence , konfigurasi saya sama dengan milik Anda. @tilgovi Saya tidak punya apa-apa lagi di depan nginx.

Saya mengalami masalah yang sama saat mencoba memperbarui dari 19.17.1 ke 19.9.0 dalam kondisi yang sama seperti @alorence

Saya memiliki masalah yang sama. Sampai 19.7.1 itu bekerja dengan soket unix. Dengan 19.8.0, 19.8.1 dan 19.9.0 skema diatur ke http, meskipun X-Forwarded-Proto diatur ke https.

Ketika saya menulis #1767 dan merilis 19.8.1, saya pasti menguji ini secara lokal. Jika ada yang bisa membuat sesuatu yang sederhana untuk mereproduksi masalah itu akan menghemat waktu investigasi saya! Mungkin repo dengan Dockerfile dengan pengaturan nginx dan gunicorn? Jika tidak, saya akan melihat ini ketika saya bisa dan saya senang untuk meninjau setiap permintaan tarik. Mohon maaf atas gangguannya.

apakah masalah ini hanya datang dengan Django? versi yang mana?

@benoitc Ini mungkin dapat direproduksi dengan versi Django apa pun. Saya pribadi melihat ini dengan Django 1.11.14 dan Django 2.0.7.

@tilgovi Saya sudah mendorong demonstrasi masalah yang sangat sederhana. Kode tersedia dari https://github.com/alorence/gunicorn-test , Ini bukan pengaturan berbasis Docker, tetapi berisi:

Proyek ini telah digunakan secara online, Anda dapat mengaksesnya dari http://gunicorn-test.exige.info dan https://gunicorn-test.exige.info. Perhatikan bahwa Anda mungkin tidak dapat mencapai versi http setelah Anda membuka versi https di browser terbaru.

Yang paling penting untuk diperhatikan adalah lingkungan wsgi.url_scheme diatur ke http bahkan ketika situs web dikunjungi dari url

File konfigurasi nginx:

server {
    listen 80;
    listen [::]:80;
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name gunicorn-test.exige.info;

    access_log /var/log/nginx/gunicorn-test.access.log;
    error_log  /var/log/nginx/gunicorn-test.error.log;

    location / {
        include proxy_params;
        proxy_pass http://unix:/var/tmp/gunicorn_tests.sock;
    }

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/exige.info/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/exige.info/privkey.pem;

    # Common SSL config
    include common_lets_encrypt;
}

Skrip permulaan

#!/usr/bin/env bash

source venv/bin/activate
python manage.py migrate
gunicorn --bind unix:/var/tmp/gunicorn_tests.sock gunicorn_https.wsgi:application

@alorence apa yang ada di proxy_params.conf Anda?

@tilgovi

~ ᐅ cat /etc/nginx/proxy_params
proxy_set_header Host $http_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;
~ ᐅ sudo nginx -v
nginx version: nginx/1.10.3

saya sudah
proxy_set_header X-Forwarded-Proto https;

Salam
Gunnar

Randall Leeds [email protected] schrieb am Do., 19. Juli 2018,
16:31:

@alorence https://github.com/alorence apa yang ada di proxy_params.conf Anda?


Anda menerima ini karena Anda berkomentar.
Balas email ini secara langsung, lihat di GitHub
https://github.com/benoitc/gunicorn/issues/1766#issuecomment-406296854 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ACu0K8NKp6Lm5Y5KdN9X6SqPmp92JVjMks5uIJgvgaJpZM4Ts9rh
.

mengalami kesalahan yang sama, tetapi komit di atas tampaknya membantu saya, yaitu mengatur proxy_set_header X-Forwarded-Proto $scheme;

@alorence terima kasih atas ALLOWED_HOSTS , tetapi saya mendapatkan hasil yang benar. wsgi.url_scheme disetel dan request.is_secure() mengembalikan True .

Saya memiliki masalah ini dan saya tidak tahu bagaimana cara memperbaikinya. Berapa banyak orang yang juga mengalami masalah ini? Saya memiliki konfigurasi yang sama dengan @alorence yang dikirim (kecuali Django==1.11.15 dan python 2.4 jika penting)

Untuk lebih jelasnya: Saya belum mereproduksi ini :-/. Jika ada yang bisa berbagi langkah-langkah untuk mereproduksi, silakan lakukan.

Saya baru saja mendorong barang ke produksi dan mengalami masalah ini sendiri. Saya harus menurunkan versi kembali ke 19.7.1 .

Berikut versi yang saya gunakan:
gunicorn 19.9.0
python 2.7.6
django 1.11.15
nginx 1.0.15

Konfigurasi nginx saya memiliki yang berikut:

        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Protocol $scheme;

Saya tidak memiliki https di lingkungan pengujian saya, tetapi saya akan melihat apakah saya dapat mengaturnya dan melacak masalahnya.

Setidaknya saya dapat dengan cepat menemukan masalah ini ketika saya melakukan pencarian untuk "Header skema kontradiktif" dan dapat dengan cepat menurunkan versi untuk memperbaiki masalah. Saya pikir masalah ini mungkin harus dibuka kembali karena tampaknya mempengaruhi beberapa orang.

Saya setuju bahwa bug dapat dibuka kembali, atau mungkin yang baru dapat dibuat, karena @zt-initech mengonfirmasi bahwa masalah aslinya telah diperbaiki oleh PR #1767

@tisdall apa lingkungan wsgi dikembalikan? Juga bagaimana Anda meluncurkan gunicorn? Bisakah Anda membagikan konfigurasi dan argumen baris perintah yang Anda gunakan?

@alorence tidak yakin untuk mengikuti, tetapi jika bug masih ada, silakan buka tiket untuk itu :)

@alorence telah membagikan konfigurasinya dalam pesan di atas dan saya memiliki baris yang sama di konfigurasi saya.

Saya meluncurkan gunicorn melalui supervisor, argumen baris perintah:

aplikasi gunicorn. wsgi:application --bind 127.0.0.1:8000 -w12 --max-requests 10000 --timeout 180

@predatell jadi masalah Anda setidaknya berbeda karena Anda tidak mengikat ke soket UNIX.

Bisakah orang lain memberikan perintah peluncuran gunicorn mereka dan konfigurasi lain yang menurut Anda dapat membantu? Jika ada masalah di sini saya sangat ingin memperbaikinya.

Detail apa pun tentang penyeimbang beban dan proxy juga dapat membantu. Misalnya, jika Anda menerapkan di Heroku, atau di AWS, atau di beberapa layanan lain. Atau bahkan jika Anda memiliki orkestra dengan perutean seperti k8s. Apa pun yang mungkin memengaruhi header yang diterima gunicorn. Juga, saya tidak tahu apakah semua orang menggunakan nginx atau tidak.

Tolong, sebanyak mungkin detail yang bisa Anda berikan. Jika memungkinkan, seluruh contoh repo dengan instruksi penerapan akan luar biasa.

Saya memiliki masalah yang hidup dengan baik di lingkungan pengujian saya sekarang sehingga saya dapat mencoba berbagai perbaikan dan melihat cara kerjanya.

perintah gunicorn (diubah untuk privasi): gunicorn -c /data/web/gunicorn_config.py -b unix:/tmp/wsgi_application.sock wsgi:application

gunicorn_config.py:

workers = 4
limit_request_line = 8190
max_requests = 8000  # number of requests before restart worker
max_requests_jitter = 500

Itu berjalan di belakang nginx sebagai proxy dengan konfigurasi berikut:

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://unix:/tmp/wsgi_application.sock/;
        proxy_redirect off;
        proxy_headers_hash_bucket_size 96;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Protocol $scheme;

@benoitc - Saya tidak yakin apa yang Anda maksud dengan "lingkungan wsgi kembali" atau bagaimana saya melaporkannya... Bisakah Anda menjelaskan di mana saya dapat menemukan info itu?

Saya mengaktifkan debugging dan melihat ini: secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}

Namun, konfigurasi nginx menyetel X-Forwarded-Protocol ke https alih-alih ssl . Mungkinkah ini penyebabnya?

Oke, itu pasti masalahnya... Saya mengubah gunicorn_config.py saya dan menambahkan yang berikut untuk memperbaikinya sementara:

secure_scheme_headers = {'X-FORWARDED-PROTOCOL': 'https', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}

Juga berkomentar di sini: https://github.com/benoitc/gunicorn/issues/1857#issuecomment -414435088

Analisis dari @tisdall benar dan menjelaskan masalah untuk @predatell juga.

Saya ingin tahu apakah salah satu dari Anda akrab dengan perangkat lunak yang menetapkan X-Forwarded-Protocol secara default dan nilai apa yang diharapkannya. Saya berasumsi bahwa secure_scheme_headers default kami diatur untuk mengharapkan ssl karena beberapa perangkat lunak di alam liar melakukan itu.

Perhatikan bahwa tidak perlu menyetel lebih dari satu tajuk ini, jadi Anda berdua dapat menghapus X-Forwarded-Protocol dan meninggalkan X-Forwarded-Proto .

Kesalahan "Header skema kontradiktif" dimaksudkan untuk mendeteksi kasus di mana proxy terbalik Anda hanya menyetel beberapa secure_scheme_headers tetapi klien jahat mencoba menipu status aman dengan menyetel yang lain berdasarkan permintaan. Kecuali Anda mengharapkan proxy terbalik lain, di luar kendali Anda, untuk menentukan header lain, tidak perlu mengatur lebih dari satu di nginx.

Saya tidak memiliki proxy_set_header X-Forwarded-Protocol di konfigurasi nginx saya, jadi dalam kasus saya, saya tidak berpikir ini adalah akar penyebab masalah. Tetapi mengikuti saran @benoitc , saya akan membuat masalah baru ASAP dengan ringkasan masalah dan investigasi, untuk membersihkan diskusi.

Terima kasih semua atas bantuan Anda

@tilgovi dan @tisdall terima kasih atas bantuannya. Saya punya masalah dengan ini di nginx saya:

proxy_set_header X-Forwarded-Protocol $scheme;

@alorence - Apakah mengubah secure_scheme_headers menjadi {'X-FORWARDED-PROTO': 'https'} menyelesaikan masalah Anda?

Sayangnya tidak ada. Lihat https://gunicorn-test.exige.info/

File konfigurasi saat ini adalah

secure_scheme_headers = {
#    'X-FORWARDED-PROTOCOL': 'ssl',
    'X-FORWARDED-PROTO': 'https',
#    'X-FORWARDED-SSL': 'on'
}

dan baris perintah yang diperbarui untuk menjalankan gunicorn adalah: gunicorn --bind unix:/var/tmp/gunicorn_tests.sock -c ./gunicorn_config.py gunicorn_https.wsgi:application

@alorence - Apa yang ada di gunicorn_https/wsgi.py ?

@alorence - BTW, saya berhasil memuat https: //gunicorn-test.exige.info /...

Aplikasi ini masih tersedia di github . Berikut adalah isi dari wsgi.py :

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gunicorn_https.settings")

application = get_wsgi_application()

Saya tidak mengerti komentar Anda selanjutnya. Saya juga dapat membuka https://gunicorn-test.exige.info/ , tetapi kontennya tidak valid:

request.is_secure: False
request.environ['wsgi.url_scheme']: http

Kita harus memiliki request.is_secure = True dan request.environ['wsgi.url_scheme']: https

Sebagai catatan, ini adalah aplikasi kecil yang dengan cepat saya tulis dan taruh online di salah satu server pengujian saya untuk menunjukkan masalahnya. Informasi lebih lanjut di https://github.com/benoitc/gunicorn/issues/1766#issuecomment -406161275

Oh, maaf .. Saya pikir pada satu titik saya melihatnya memberikan kesalahan "Header skema kontradiktif" alih-alih konten. Saya juga lupa Anda menyebutkan kode itu di github.

Ya, saya memeriksa dan saya mengatur dengan benar request.is_secure menjadi True dan request.environ['wsgi.url_scheme'] menjadi 'https' .

Bagaimana jika Anda secara eksplisit menambahkan proxy_set_header X-Forwarded-Proto $scheme; ke konfigurasi nginx? Saya pikir nginx mungkin tidak secara otomatis menggunakan apa yang ada di proxy_params dan Anda harus benar-benar menyertakan dengan include proxy_params; di blok location / .

eh.. Anda memiliki pernyataan include sana.. Saya pikir saya perlu istirahat. ^_^

Saya mendorong modifikasi ini ke file konfigurasi nginx, dan memulai ulang aplikasi. Hasilnya tetap sama. https://gunicorn-test.exige.info/

server {
    listen 80;
    listen [::]:80;
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name gunicorn-test.exige.info;

    access_log /var/log/nginx/gunicorn-test.access.log;
    error_log  /var/log/nginx/gunicorn-test.error.log;

    location / {
        proxy_set_header Host $http_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;
        proxy_pass http://unix:/var/tmp/gunicorn_tests.sock;
    }

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/exige.info/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/exige.info/privkey.pem;

    # Common SSL config
    include common_lets_encrypt;
}

membuat frustrasi! Ada petunjuk di request.META ?

Saya harus memperbarui sumber proyek untuk mencetak konten request.META di lingkungan ini. Sayangnya, saya tidak akan dapat melakukan ini sebelum 2 hari. Maaf tentang itu. Sesegera mungkin, saya akan memperbarui proyek, memulai kembali di server uji, dan mungkin membuka masalah baru sehingga kami dapat melanjutkan diskusi dalam tiket terbuka ... Terima kasih banyak atas bantuan Anda

Oke. Tandai saya pada edisi baru dan saya akan memeriksanya ketika Anda telah mengubahnya...

@alorence Saya memiliki masalah yang sama, bagaimana Anda mengatasinya? Sekarang saya menurunkan versi gunicorn dari 19.9.0 menjadi 19.7.1 .

Perbaikan telah didorong ke cabang utama, tetapi belum dirilis untuk saat ini. Versi 19.9 masih memiliki masalah. Anda dapat mengikuti # 1861 untuk informasi terbaru.
Di pihak saya, saya memilih untuk beralih dari soket Unix ke soket HTTP untuk sebagian besar situs web saya, jadi saya tidak terpengaruh oleh masalah ini lagi.

Saya juga menghabiskan beberapa waktu untuk melacak akar masalah ini setelah menabrak gunicorn dari 19.8.0 ke 19.9.0 di salah satu envs saya. Karena ini adalah regresi yang cukup serius, saya terkejut belum ada rilis 19.9.1?

@villebro masalah apa yang kamu bicarakan? Jika Anda berbicara tentang #1861 atau #1882, Anda mungkin harus berkomentar di sana. (Saya pikir itu satu-satunya hal yang tidak tersedia di versi terbaru)

@villebro saya minta maaf. Kita semua memiliki waktu dan perhatian yang terbatas untuk diberikan kepada Gunicorn. Kami sedang bekerja keras untuk mengeluarkan versi 20.

@tisdall Anda benar, saya mengacu pada #1861 (saya pikir). Saya akan berkomentar dalam masalah yang benar. @tilgovi tolong jangan salah paham, saya sepenuhnya mengerti bahwa para pengembang memiliki waktu terbatas dan tidak bermaksud terdengar tidak berterima kasih atas pekerjaan yang sedang dilakukan. Dalam kasus saya, regresi membuat 19.9.0 tidak dapat digunakan, dan saya berharap ini menjadi kasus bagi banyak pengguna gunicorn. Secara pribadi saya lebih suka melihat rilis 19.9.1 dengan semua regresi besar yang diketahui diperbaiki (= di-backport dari master) daripada rilis utama baru 20.0.0.

@villebro jangan khawatir sama sekali. Saya, lebih dari segalanya, hanya memastikan Anda mendengar beberapa tanggapan sehingga Anda tahu bahwa Anda didengar.

Saya seharusnya menyebutkan bahwa kami berkomitmen untuk mengeluarkan satu lagi rilis 19.x sehingga kami bisa mendapatkan beberapa perbaikan sebelum kami menghentikan dukungan Python 2.

Lihat # 2022 dan beri tahu kami jika kami melewatkan sesuatu.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat