การควบคุมเป้าหมายพร็อกซี Nginx โดยใช้คุกกี้หรือไม่


11

ฉันพยายามแปลงพร็อกซีย้อนกลับโดยใช้การตั้งค่า Apache mod_rewrite ที่น่าสนใจเพื่อใช้ Nginx แทน (เนื่องจากความกังวลภายนอกที่เราย้ายจาก Apache เป็น Nginx และทุกอย่างทำงานได้ดียกเว้นส่วนนี้)

การตั้งค่าดั้งเดิมของฉันคือการอ่านคุกกี้ HTTP (กำหนดโดยแอพพลิเคชั่นบางตัว) และขึ้นอยู่กับค่าของมัน มันเป็นอะไรเช่นนี้:

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]

ฉันพยายามทำสิ่งเดียวกันโดยใช้ Nginx และการกำหนดค่าเริ่มต้นของฉันเป็นแบบนี้ (โดยที่ "proxy_override" เป็นชื่อของคุกกี้):

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;
}

แต่มันก็ไม่ได้ ฉันพยายามดูว่า Nginx สามารถอ่านคุกกี้ของฉันได้หรือไม่โดยเขียนพร็อกซีหลักเพื่อเปลี่ยนเส้นทางไปยังบางสิ่งตาม${cookie_proxy_override}และฉันเห็นว่ามันอ่านเนื้อหาได้ดี แต่ifดูเหมือนว่าจะล้มเหลวเสมอ

ความพยายามครั้งต่อไปของฉันตามคำตอบของ Rikih คือ:

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;
}

และตอนนี้ฉันเห็นได้ว่าifบล็อกถูกเปิดใช้งาน แต่แทนที่จะแสดงคำขอ (เช่นฉันคิดว่ามันจะทำ) มันส่งคืน 302 การเปลี่ยนเส้นทางไปยัง URL ที่ระบุ - ซึ่งไม่ใช่สิ่งที่ฉันพยายามทำ: ฉันต้องการเซิร์ฟเวอร์ เพื่อถ่ายทอดคำขอไปยังแบ็กเอนด์อย่างโปร่งใสและตอบสนองไปยังไคลเอนต์เดิม

ผมทำอะไรผิดหรือเปล่า?

คำตอบ:


16

คล้ายกับคำตอบนี้ วิธีสำนวน Nginx mapเพื่อชนิดของปัญหานี้ผ่านทาง

โดยทั่วไปคุณจะกำหนดส่วนmapในhttp

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

จากนั้นคุณก็ใช้$my_upstreamในlocationส่วน:

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

Nginx ประเมินตัวแปรแผนที่อย่างเกียจคร้านเพียงครั้งเดียว (ต่อคำขอ) และเมื่อคุณใช้งาน


3
ขอขอบคุณที่เป็นวิธีการที่ดีกว่าแล้วเหมืองสะดุดตาที่สุดเพราะผมสามารถใช้ตัวแปรคุกกี้ชื่อโดยตรง (ไม่แน่ใจว่าทำไมฉันไม่สามารถในif) และฉันก็ดำเนินการ แม้ว่าจะมีปัญหาหนึ่ง - Nginx (อย่างน้อยรุ่นของฉัน: 1.0.0) ไม่ชอบการจับภาพหมายเลขmapดังนั้นฉันจึงต้องใช้~^(?P<name>[\w-]+) $name;แทน ฉันได้แก้ไขคำตอบของคุณแล้ว
Guss

3

ในที่สุดโซลูชันของฉันก็ลดลงมาถึงสิ่งนี้:

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
    }
}

การทดสอบจะดำเนินการในserverขอบเขตสำหรับแต่ละคำขอ (ก่อนการเปลี่ยนเส้นทางที่แท้จริงจะได้รับการแก้ไข) และใช้เพื่อตั้งค่าตัวแปร - นี่เป็นการใช้งานที่ได้รับการสนับสนุนของโมดูล Nginx "rewrite" นอกจากนี้ยังทดสอบทั้งหมด$http_cookieเช่น @Rikih ที่แนะนำ แต่มีชื่อของคุกกี้เพื่อให้แน่ใจว่าฉันไม่ตรงกับสิ่งที่สุ่มที่ผู้คนอาจจะขว้างใส่ฉัน

จากนั้นในlocationขอบเขตที่ฉันต้องการเปลี่ยนเส้นทางฉันใช้ชื่อตัวแปรที่มีการกำหนดค่าอัปสตรีมเริ่มต้นหรือถูกเขียนทับโดยคุกกี้


0

คุณลอง $ http_cookie แล้วหรือยัง http://wiki.nginx.org/HttpRewriteModule

ถ้า ($ http_cookie ~ * "proxy-target-A") {foo; }


ใช้งานได้จริงกับการทดสอบแม้ว่าฉันจะไม่แน่ใจว่าทำไมฉันไม่สามารถทดสอบชื่อคุกกี้เฉพาะได้ สิ่งที่ฉันไม่ชอบคือการที่rewriteไม่ทำการเขียนพร็อกซีใหม่ แต่กลับส่งการเปลี่ยนเส้นทางไปยังไคลเอนต์แทนและฉันไม่สามารถใช้ proxy_pass ในifบล็อกได้ ฉันได้ปรับปรุงคำถามตาม
Guss

0

ฉันมีตัวอย่างที่ฉันใช้เพื่อตรวจสอบส่วนหัวคำขอตาม udid และใช้งานได้อาจเป็นไปได้ว่าคุณจะได้รับความคิด

   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;
    }

คุณใช้ Nginx เวอร์ชันใดอยู่ ฉันใช้ 1.0 และเมื่อฉันใช้ proxy_pass ตามที่คุณระบุไว้ที่นี่ฉันจะได้รับข้อความแสดงข้อผิดพลาดนี้: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

ฉันใช้ nginx-0.8.53-1.el5
chocripple

อาจเป็นคุณต้องการที่จะดูฟอรั่ม. nginx.org/read.php?2,13955,15981
chocripple

ทางออกในฟอรัมคือไม่เปลี่ยน URI คำขอเมื่อพร็อกซีไปยังเซิร์ฟเวอร์อื่น แต่นั่นคือสิ่งที่ฉันต้องทำ - เขียน URI คำขอใหม่เพื่อกำหนดเป้าหมายแอปพลิเคชันที่แตกต่างจาก URL ดั้งเดิม นอกจากนี้ตัวอย่างของคุณดูเหมือนจะใช้ URI คำขอในproxy_passคำสั่งด้วยดังนั้นฉันไม่แน่ใจว่าจะสามารถทำงานให้คุณได้อย่างไรจากการอภิปรายในฟอรัมด้านบน
Guss
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.