Mengontrol target proxy Nginx menggunakan cookie?

11

Saya mencoba untuk mengonversi proksi terbalik menggunakan pengaturan mod_rewrite Apache yang menarik untuk menggunakan Nginx sebagai gantinya (karena kekhawatiran eksternal kami bergerak dari Apache ke Nginx, dan sebagian besar semuanya berfungsi dengan baik kecuali bagian ini).

Pengaturan awal saya adalah membaca cookie HTTP (ditetapkan oleh beberapa aplikasi) dan tergantung pada nilainya, arahkan proxy terbalik ke backend yang berbeda. Bunyinya kira-kira seperti ini:

RewriteCond %{HTTP_COOKIE}  proxy-target-A
RewriteRule ^/original-request/ http://backend-a/some-application [P,QSA]

RewriteCond %{HTTP_COOKIE}  proxy-target-B
RewriteRule ^/original-request http://backend-b/another-application [P,QSA]

RewriteRule ^/original-request http://primary-backend/original-application [P,QSA]

Saya mencoba untuk mencapai hal yang sama menggunakan Nginx, dan konfigurasi awal saya adalah sesuatu seperti ini (di mana "proxy_override" adalah nama cookie):

location /original-request {
    if ($cookie_proxy_override = "proxy-target-A") {
        rewrite . http://backend-a/some-application;
        break;
    }
    if ($cookie_proxy_override = "proxy-target-B") {
        rewrite . http://backend-b/another-application;
        break;
    }
    proxy_pass http://primary-backend/original-application;
}

Tapi ternyata tidak. Saya sudah mencoba untuk melihat apakah Nginx dapat membaca cookie saya dengan menulis proksi primer untuk mengalihkan ke sesuatu berdasarkan ${cookie_proxy_override}dan saya dapat melihat bahwa itu membaca konten baik-baik saja, tetapi iftampaknya selalu gagal.

Percobaan saya berikutnya, menurut jawaban Rikih adalah ini:

location /original-request {
    if ($http_cookie ~ "proxy-target-A") {
        rewrite . http://backend-a/some-application;
        break;
    }
    if ($http_cookie ~ "proxy-target-B") {
        rewrite . http://backend-b/another-application;
        break;
    }
    proxy_pass http://primary-backend/original-application;
}

Dan sekarang saya dapat melihat bahwa ifblok diaktifkan, tetapi alih-alih memproksi permintaan (seperti yang saya pikir akan lakukan) ia mengembalikan 302 redirect ke URL yang ditentukan - yang bukan apa yang saya coba lakukan: Saya perlu server untuk menyampaikan permintaan secara transparan ke backend dan mengirimkan respons ke klien asli.

Apa yang saya lakukan salah?

Guss
sumber

Jawaban:

16

Mirip dengan jawaban ini . Pendekatan idiomatik Nginx untuk masalah seperti ini adalah via map.

Pada dasarnya, Anda mendefinisikan bagian mapdalamhttp

map $cookie_proxy_override $my_upstream {
  default default-server-or-upstream;
  ~^(?P<name>[\w-]+) $name;
}

Maka Anda cukup menggunakan $my_upstreamdi locationbagian:

location /original-request {
  proxy_pass http://$my_upstream$uri;
}

Nginx mengevaluasi variabel peta dengan malas, hanya sekali (per permintaan) dan ketika Anda menggunakannya.

Alexander Azarov
sumber
3
Terima kasih, itu pendekatan yang lebih baik daripada saya, terutama karena saya dapat menggunakan variabel cookie bernama secara langsung (tidak yakin mengapa saya tidak bisa masuk if) dan saya menerapkannya. Namun ada satu masalah - Nginx (setidaknya versi saya: 1.0.0) tidak suka menangkap bernomor map, jadi saya harus menggunakannya ~^(?P<name>[\w-]+) $name;sebagai gantinya. Saya telah mengedit jawaban Anda sesuai dengan itu.
Guss
3

Akhirnya solusi saya intinya adalah:

server {
    ...
    set $upstream "default-server-or-upstream";
    if ($http_cookie ~ "proxy_override=([\w-]+)") {
        set $upstream $1;                                   
    }

    location /original-request {
        proxy_pass http://$upstream/original-application
    }
}

Tes ini dilakukan dalam serverruang lingkup untuk setiap permintaan (sebelum pengalihan aktual diselesaikan) dan hanya digunakan untuk mengatur variabel - ini tampaknya merupakan penggunaan modul "menulis ulang" Nginx yang didukung. Ini juga menguji keseluruhan $http_cookieseperti yang disarankan @Rikih, tetapi menyertakan nama cookie untuk memastikan saya tidak cocok dengan hal-hal acak yang mungkin dilontarkan orang kepada saya.

Kemudian dalam locationlingkup di mana saya ingin melakukan redirect, saya menggunakan nama variabel yang berisi konfigurasi hulu default atau ditimpa oleh cookie.

Guss
sumber
0

Sudahkah Anda mencoba $ http_cookie? http://wiki.nginx.org/HttpRewriteModule

if ($ http_cookie ~ * "proxy-target-A") {foo; }

chocripple
sumber
Itu memang berhasil untuk pengujian, meskipun saya tidak yakin mengapa saya tidak bisa hanya menguji terhadap nama cookie tertentu. Apa yang saya tidak suka adalah bahwa rewritesebenarnya tidak melakukan penulisan ulang proxy tetapi mengembalikan redirect ke klien, dan saya tidak bisa menggunakan proxy_pass di ifblok. Saya telah memperbarui pertanyaannya.
Guss
0

Saya punya sampel yang saya gunakan untuk mendeteksi header permintaan berdasarkan udid dan itu berfungsi, mungkin Anda akan mendapatkan beberapa ide.

   location / {
      proxy_set_header Host $http_host;
  if ($request_uri ~ ^/(.*)udid=xxxxxxxxxxxxxx(.*)$) {
    proxy_pass   http://1.1.1.1$request_uri;
    break;
  }
  if ($request_uri ~ ^/(.*)udid=yyyyyyyyyyyyyy(.*)$) {
    proxy_pass   http://3.3.3.3$request_uri;
    break;
  }
       proxy_pass http://2.2.2.2$request_uri;
    }
chocripple
sumber
Versi Nginx mana yang Anda gunakan? Saya menggunakan 1.0 dan ketika saya menggunakan proxy_pass seperti yang Anda tentukan di sini, saya mendapatkan pesan kesalahan ini:nginx: [emerg] "proxy_pass" may not have URI part in location given by regular expression, or inside named location, or inside the "if" statement, or inside the "limit_except" block in /etc/nginx/conf.d/proxy.conf:47
Guss
saya menggunakan nginx-0.8.53-1.el5
chocripple
mungkin Anda ingin melihat forum.nginx.org/read.php?2,13955,15981
chocripple
Solusi di forum adalah untuk tidak mengubah URI permintaan saat proxy ke server lain, tetapi itulah yang harus saya lakukan - menulis ulang permintaan URI untuk menargetkan aplikasi yang berbeda dari yang disertakan oleh URL asli. Selain itu, contoh Anda tampaknya juga menggunakan URI permintaan dalam proxy_passperintah, jadi saya tidak yakin bagaimana cara kerjanya untuk Anda mengingat diskusi forum di atas.
Guss