nginx: วิธีการป้องกันการบล็อกเซิร์ฟเวอร์ SSL ที่มีชื่อตรงว่าทำหน้าที่เป็น catchall สำหรับ SSL ทั้งหมด


17

ฉันมีเว็บเซิร์ฟเวอร์ที่มีเซิร์ฟเวอร์เสมือนมากมาย หนึ่งในนั้นคือ SSL ปัญหาคือเนื่องจากไม่มีเซิร์ฟเวอร์ catchall ที่คอยรับฟัง SSL คำขอ https ไปยังเว็บไซต์อื่น ๆ จะถูกส่งโดย 1 SSL block

การกำหนดค่าของฉันเป็นหลักดูเหมือนว่านี้:

# the catch all
server {
  listen 80 default;

  # I could add this, but since I have no default cert, I cannot enable SSL,
  # and this listen ends up doing nothing (apparently).
  # listen 443; 

  server_name _;
  # ...
}

# some server
server {
  listen 80;
  server_name server1.com;
  # ...
}

# some other server ...
server {
  listen 80;
  server_name server2.com;
  # ...
}

# ... and it's https equivalent
server {
  listen 443;
  ssl on;
  server_name server2.com;
  # ...
}

ในขณะนี้เนื่องจากไม่มีผู้ฟังเริ่มต้นสำหรับ 443 คำขอเช่นhttps://server1.comนั้นจะถูกแสดงผลโดยserver2.comบล็อค https นี่เป็นไปตามตรรกะสำหรับserver_nameในเอกสาร

หากไม่มีการจับคู่เซิร์ฟเวอร์ {... } บล็อกในไฟล์การกำหนดค่าจะถูกใช้ตามลำดับต่อไปนี้:

  1. บล็อกเซิร์ฟเวอร์ที่มีคำสั่งฟังตรงที่ทำเครื่องหมายเป็น [เริ่มต้น | default_server]
  2. บล็อกเซิร์ฟเวอร์แรกที่มีคำสั่ง Listen ที่ตรงกัน (หรือนัย Listen 80;)

ทางออกที่ดีที่สุดสำหรับปัญหานี้คืออะไร? ฉันจำเป็นต้องตั้งค่าตัวแทนจำลองสำหรับการจับบล็อกเซิร์ฟเวอร์ทั้งหมดเพื่อให้ฉันสามารถฟัง 443 และจัดการคำขอที่ไม่ดีได้หรือไม่ มีพารามิเตอร์ที่ฉันไม่รู้ของกองกำลังที่ตรงกับชื่อโฮสต์ที่แน่นอนด้วยserver?


คุณต้องการเกิดอะไรขึ้นเมื่อผู้คนพยายามเข้าถึงเว็บไซต์อื่นโดยใช้ https
David Schwartz

โดยหลักการแล้วฉันต้องการให้ nginx ไม่ให้บริการ https เลยเว้นแต่ว่าชื่อโฮสต์ตรงกันหรือจะเปลี่ยนเส้นทางไปยัง http ที่โฮสต์เดียวกัน
หมายเลข 1311407

คำตอบ:


9

โดยหลักการแล้วฉันต้องการให้ nginx ไม่ให้บริการ https เลยเว้นแต่ว่าชื่อโฮสต์ตรงกันหรือจะเปลี่ยนเส้นทางไปยัง http ที่โฮสต์เดียวกัน

ไม่สามารถทำได้ การเชื่อมต่อจากไคลเอนต์ที่ไปที่https://foo.example.com/ไม่สามารถยอมรับได้โดยอะไรนอกจากใบรับรอง SSL ที่มี "foo.example.com" เป็นชื่อใดชื่อหนึ่ง จะไม่มีโอกาสเปลี่ยนเส้นทางจนกว่าจะยอมรับการเชื่อมต่อ SSL

หากคุณกำหนดค่าแต่ละไซต์สำหรับ SSL ผู้ใช้ที่คลิกผ่านข้อผิดพลาดใบรับรองจะได้รับไซต์ที่พวกเขาร้องขอ หากคุณกำหนดค่าไซต์ "catch all" สำหรับ SSL ที่ให้เฉพาะหน้าข้อผิดพลาดและกำหนดค่าการโฮสต์เสมือนแบบอิงชื่อสำหรับไซต์เดียวที่ควรสนับสนุน SSL คุณสามารถให้บริการหน้าข้อผิดพลาดแก่ลูกค้า

SSL และ HTTP โฮสติ้งเสมือนไม่เล่นด้วยกัน


นี่คือสิ่งที่ฉันรวบรวมหลังจากอ่านเอกสาร ฉันแค่หวังว่าฉันจะพลาดบางสิ่งบางอย่าง ฉันไม่สนใจคำเตือน SSL เลย ฉันแค่ไม่ต้องการให้ใครบางคนป้อนserver1.comและค้นหาตัวเองดูที่หน้าแรกของ server2.com ... ไม่มีวิธีบอก nginx ว่าไม่ยอมรับคำขอหรือไม่
หมายเลข 1311407

หากไม่ยอมรับคำขอไซต์แรกจะไม่ทำงาน ต้องยอมรับคำขอเพื่อค้นหาว่าไซต์ใดที่ผู้ใช้พยายามเข้าถึง
David Schwartz

2
"การเชื่อมต่อจากไคลเอนต์ที่ไปที่foo.example.comไม่สามารถยอมรับได้ยกเว้นใบรับรอง SSL ที่มี" foo.example.com "เป็นชื่อใดชื่อหนึ่ง" - สิ่งนี้ไม่ถูกต้องเซิร์ฟเวอร์จะยอมรับคำขอและขึ้นอยู่กับลูกค้าเพื่อตรวจสอบว่า DN ที่ร้องขอตรงกับใบรับรองเซิร์ฟเวอร์ DN
ColinM

4

วิธีเดียวที่จะทำได้คือการสร้างใบรับรอง SSL ที่ลงชื่อด้วยตนเองและใช้เพื่อรับการควบคุมคำขอ https ที่เข้ามา คุณสามารถสร้างใบรับรอง SSL ที่ลงชื่อด้วยตนเองได้ในขั้นตอนง่ายๆไม่กี่ขั้นตอนที่ระบุไว้ในโพสต์นี้

สมมติว่าคุณสร้างใบรับรองที่ลงชื่อด้วยตัวเองด้วยชื่อไฟล์ของ server.crt จากนั้นคุณจะต้องผนวกสิ่งต่อไปนี้ในการกำหนดค่า nginx ของคุณ:

server {
    listen  443;

    ssl    on;
    ssl_certificate         /etc/nginx/ssl/server.crt;
    ssl_certificate_key     /etc/nginx/ssl/server.key;

    server_name server1.com;

    keepalive_timeout 60;
    rewrite ^       http://$server_name$request_uri? permanent;
}

คุณจะยังได้รับข้อความเตือน SSL ของเบราว์เซอร์ แต่อย่างน้อยคุณจะสามารถควบคุมสิ่งที่เกิดขึ้นต่อไปได้


1
ฉันเห็นด้วยกับสิ่งนี้ มีปัญหาของเบราว์เซอร์ที่แสดงข้อความเตือนเกี่ยวกับใบรับรองที่ไม่น่าเชื่อถือ แต่หากคุณพยายามป้องกันไม่ให้ผู้ใช้ไปที่ https: // <ip-address> เพื่อให้เซิร์ฟเวอร์ได้รับใบรับรองที่ไม่ถูกต้องเท่ากันสำหรับ vhosts จริงของคุณ (ไม่ถูกต้อง เนื่องจากชื่อโฮสต์จะไม่ตรงกัน) ดังนั้นคุณควรแสดงใบรับรองดัมมี่ที่ลงชื่อด้วยตนเองไม่ถูกต้อง แบบนั้นบอกพวกเขาว่า "ไม่มีอะไรให้ดูที่นี่ไม่แม้แต่ใบรับรองจากโฮสต์อื่น"
Daniel F

2

เพิ่มเซิร์ฟเวอร์บล็อก catch-all และส่งคืนรหัสสถานะ 444 ซึ่งจะบอกให้ nginx ปิดการเชื่อมต่อก่อนที่จะส่งข้อมูลใด ๆ

server {
    listen 443 default_server ssl;
    server_name _;
    return 444;
}

1

วันนี้คุณสามารถใช้ส่วนขยายบ่งชี้ชื่อเซิร์ฟเวอร์ TLS (SNI, RFC 6066) ฟัง HTTPS จะสามารถรับรู้ชื่อโดเมนก่อนที่จะให้บริการใบรับรองที่เหมาะสม

ซึ่งหมายความว่าคุณจะต้องมีใบรับรองสำหรับโดเมนทั้งหมดของคุณและเมื่อใช้ SNI เพื่อรับรู้หนึ่งในโดเมนอื่น ๆ คุณสามารถใช้ HTTP 301 redirect ไปยัง HTTP เวอร์ชันที่ไม่มีการเข้ารหัสเว้นแต่ชื่อเซิร์ฟเวอร์จะตรงกับที่ต้องการ การเข้ารหัส

ข้อมูลเพิ่มเติมเกี่ยวกับ SNI ที่มีอยู่ในเอกสารของ nginx http://nginx.org/en/docs/http/configuring_https_servers.html


0

แม็พชื่อโฮสต์ที่ร้องขอกับชื่อโฮสต์ที่ถูกต้องในhttp {}บล็อก:

map $ssl_server_name $correct_hostname_example {
  default 0;
  example.com 1;
  www.example.com 1;
}

แล้วในการserver {}เชื่อมต่อบล็อกฆ่าด้วยชื่อโฮสต์ผิด:

if ($correct_hostname_example = 0) {
  return 444;
}

ใช้หลายแผนที่ตามความจำเป็นสำหรับเซิร์ฟเวอร์หลาย ๆ บล็อค การเชื่อมต่อจะยังคงถูกสร้างขึ้นโดยใช้หนึ่งในใบรับรองของคุณ แต่ถ้าบล็อกสุดท้ายนี้มีอยู่ในทุก ๆ บล็อคเซิร์ฟเวอร์ที่ให้บริการ SSL คุณจะต้อง "ปิด" การเชื่อมต่อที่มีชื่อโฮสต์ที่ไม่ถูกต้อง อาจจำเป็นในบล็อกเซิร์ฟเวอร์แรกเท่านั้น แต่การเพิ่มลงในบล็อกเซิร์ฟเวอร์ทั้งหมดจะทำให้แน่ใจว่าลำดับนั้นไม่สำคัญ

$ssl_server_nameตัวแปรอยู่ใน Nginx 1.7 หรือสูงกว่า


0

นี่คือวิธีที่ฉันแก้ไขปัญหา:

  1. สร้างใบรับรองที่ลงนามเอง:

openssl req -nodes -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -days 3650

  1. คัดลอกไปที่ NginX สามารถค้นหาได้:

cp self*.pem /etc/nginx/ssl/

  1. ตั้งค่าเส้นทาง catch-all:
server {
    listen 443 default_server ssl;

    ssl on;
    ssl_certificate /etc/nginx/ssl/self_cert.pem;
    ssl_certificate_key /etc/nginx/ssl/self_key.pem;

    return 301 http://$host;
}

มันจะทำอะไร: มันจะเตือนคุณ (ไม่ทางใด ๆ ) บนเซิร์ฟเวอร์ใด ๆ ที่ไม่มีใบรับรองของตัวเอง แต่คำเตือนจะไม่พูดชื่อใบรับรองผิด หากผู้ใช้คลิกที่ "เยี่ยมชมต่อไป" พวกเขาจะถูกเปลี่ยนเส้นทางไปยังเว็บไซต์ที่ไม่ใช่รุ่น ssl ที่พวกเขาพิมพ์

ข้อแม้ :

หากไซต์ที่เปิดใช้งาน SLL ของคุณกำหนดเท่านั้นwww.example.com(และไม่ใช่example.com) เส้นทางที่เป็นเส้นทางทั้งหมดของคุณจะสิ้นสุดการให้บริการhttps://example.comด้วยใบรับรองที่ลงชื่อด้วยตนเองและคำเตือนที่เกี่ยวข้อง


-2

เปลี่ยนเส้นทางไปที่ http:

server {
    listen       443;
    server_name  *.com;
    rewrite        ^ http://$host$request_uri? permanent;
}    

ผลตอบแทน 404:

server {
    listen       443;
    server_name  *.com;
    return 404;
}    

1
สิ่งนั้นจะยังคงส่งผลให้มีการเตือน SSL เนื่องจากต้องสร้างช่องสัญญาณ SSL ก่อนที่การเปลี่ยนเส้นทาง HTTP จะเกิดขึ้น ดูคำตอบที่ยอมรับของ David Schwartz
cjc
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.