การตั้งค่าเซิร์ฟเวอร์ nginx“ เริ่มต้น” อย่างถูกต้องสำหรับ https


70

ฉันมีเซิร์ฟเวอร์หลายเครื่องที่ทำงานในเครื่องเดียวกันบางเครื่องมี http เท่านั้นบางเครื่องมีทั้ง http และ https มีบล็อกเซิร์ฟเวอร์จำนวนมากที่กำหนดไว้ในไฟล์แยกต่างหากซึ่งรวมอยู่ในไฟล์กำหนดค่าหลัก

ฉันได้ตั้งค่าเซิร์ฟเวอร์ "เริ่มต้น" สำหรับ http ซึ่งจะให้บริการ "หน้าการบำรุงรักษา" ทั่วไปกับคำขอที่ไม่ตรงกับชื่อ server_name อื่น ๆ ในไฟล์ config อื่น ๆ http เซิร์ฟเวอร์เริ่มต้นทำงานตามที่คาดไว้จะใช้ server_name "_" และปรากฏเป็นครั้งแรกในรายการรวม (เพราะฉันสังเกตว่าในกรณีของซ้ำ server_name ข้ามเซิร์ฟเวอร์หนึ่งที่ปรากฏก่อนใช้) มันใช้งานได้ดี

ฉันคาดหวังว่าบล็อกเซิร์ฟเวอร์ที่แน่นอนเหมือนกัน (เฉพาะการสลับ "Listen 80 default_server" เป็น "Listen 443 default_server" และแทนการแสดงหน้า "return 444") อย่างไรก็ตามมันไม่ได้ แต่ดูเหมือนว่าเซิร์ฟเวอร์ https เริ่มต้นใหม่จะดึงการเชื่อมต่อ https ขาเข้าทั้งหมดและทำให้พวกเขาล้มเหลวแม้ว่าบล็อกเซิร์ฟเวอร์อื่นจะมี server_names ที่เหมาะสมกว่าสำหรับคำขอขาเข้า การลบเซิร์ฟเวอร์ https เริ่มต้นใหม่จะทำให้การทำงานแบบกึ่งถูกต้องกลับมาทำงานต่อ: เว็บไซต์ที่มี https จะโหลดได้อย่างถูกต้อง แต่เว็บไซต์ที่ไม่มี https จะถูกส่งไปยังเซิร์ฟเวอร์ https แรกในไฟล์รวม (ซึ่งตามเอกสารหากไม่มี "default_server" ปรากฏขึ้นบล็อกเซิร์ฟเวอร์แรกที่ปรากฏจะเป็น "ค่าเริ่มต้น")

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

นี่คือ "เซิร์ฟเวอร์เริ่มต้น" ของฉัน http ทำงานโดยไม่ทำลายเซิร์ฟเวอร์อื่น ๆ Https แบ่งเซิร์ฟเวอร์อื่น ๆ และใช้ทั้งหมด

server {
    listen 443 ssl default_server;
    server_name _;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    return 444;
}

server {
    listen *:80 default_server;
    server_name _;
    charset utf-8;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    root /home/path/to/templates;

    location / {
        return 503;
    }

    error_page 503 @maintenance;

    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
    }
}

มีใครเห็นบ้างไหมว่ามีอะไรผิดปกติที่นี่?

ssl  https  nginx 

คำตอบ:


27

คุณไม่มี ssl_certificate หรือ ssl_certificate_key ใด ๆ ที่กำหนดไว้ในบล็อก https "เริ่มต้น" ของคุณ แม้ว่าคุณจะไม่มีหรือต้องการคีย์จริงสำหรับสถานการณ์เริ่มต้นนี้ แต่คุณยังจำเป็นต้องกำหนดค่าอย่างน้อยหนึ่งอย่าง nginx จะมีพฤติกรรมที่ไม่พึงประสงค์ที่คุณอธิบาย

สร้างใบรับรองที่ลงนามเองด้วยชื่อสามัญของ * แล้วเสียบลงในการกำหนดค่าของคุณและจะเริ่มทำงานตามที่คุณต้องการ

พฤติกรรม "เริ่มต้น" ภายใต้การตั้งค่านี้จะเป็นที่เบราว์เซอร์จะได้รับคำเตือนว่าใบรับรองไม่สามารถเชื่อถือได้หากผู้ใช้เพิ่มใบรับรองเป็นข้อยกเว้นการเชื่อมต่อจะถูกลดลงโดย nginx และพวกเขาจะเห็นค่าเริ่มต้นของเบราว์เซอร์ ข้อความแสดงข้อผิดพลาด "ไม่สามารถเชื่อมต่อได้"


1
ฉันได้ลองแล้ว แต่ก็ยังใช้งานไม่ได้: ssl ทั้งหมดร้องขอไปที่ IP ไปยังโฮสต์ ssl อื่นของฉัน มีอะไรอีกบ้างที่ฉันจะลอง
Michael Härtl

22

ฉันจัดการเพื่อกำหนดค่าโฮสติ้งเฉพาะที่ใช้ร่วมกันบน IP เดียวที่มี nginx HTTP และ HTTPS เริ่มต้นให้บริการ 404 สำหรับโดเมนที่ไม่รู้จักที่เข้ามา

1 - สร้างโซนเริ่มต้น

ในฐานะที่เป็น Nginx vhosts โหลดเพื่อ ASCII, คุณควรสร้าง00-defaultไฟล์ / /etc/nginx/sites-enabledเชื่อมโยงสัญลักษณ์ลงใน

2 - กรอกโซนเริ่มต้น

เติม00-defaultvhosts เริ่มต้นของคุณ นี่คือโซนที่ฉันใช้อยู่:

server {
    server_name _;
    listen       80  default_server;
    return       404;
}


server {
    listen 443 ssl;
    server_name _;
    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    return       404;
}

3 - สร้าง certif ที่ลงชื่อด้วยตนเองทดสอบและโหลดซ้ำ

/etc/nginx/ssl/nginx.crtคุณจะต้องสร้างใบรับรองตนเองลงนามใน

สร้างใบรับรองที่ลงนามด้วยตนเองเริ่มต้น:

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

เพียงเตือนความจำ:

  • ทดสอบการกำหนดค่า nginx ก่อนที่จะโหลด / รีสตาร์ท: nginx -t
  • โหลดความสนุกใหม่: sudo service nginx reload

หวังว่ามันจะช่วย


2
ไม่ได้แก้คำเตือนเบราว์เซอร์ของการเยี่ยมชมเว็บไซต์ที่ไม่ปลอดภัย
gdbj

4
ไม่สามารถแก้ไขได้เนื่องจาก catch-all ต้องตรงกับโดเมนใด ๆ ไม่มี SSL ที่สามารถแทนที่โดเมนทั้งหมดได้ ลองนึกภาพถ้าคุณปลอมแปลงที่อยู่ google.fr บนเซิร์ฟเวอร์ของคุณคุณจะสามารถตรวจสอบสิทธิ์เซิร์ฟเวอร์เป็น google.fr ได้ มันจะเป็นปัญหาด้านความปลอดภัยที่ร้ายแรง :(
ถ้า Notnot 9'17

ใช่ฉันเดาว่ามันสมเหตุสมผลแล้ว น่าเสียดายที่ใน Chrome คุณจะได้รับคำเตือนที่น่ากลัวก่อนที่จะเห็นหน้า 404 ซึ่งแย่กว่าการให้เซิร์ฟเวอร์ปฏิเสธการรับส่งข้อมูล ทำให้ดูเหมือนว่าเซิร์ฟเวอร์กำหนดค่าผิดพลาด
gdbj

ขอบคุณมาก. นี้ทำงานให้ฉันยกเว้นว่าผมต้องเพิ่มdefault_serverสำหรับฟัง 443 และฉันเพิ่มอยู่ IPv6 [::]: 80 [::]: 443 default_serverกับ
chmike

16

โดยทั่วไปเราต้องการหลีกเลี่ยงค่าใช้จ่ายทั้งหมดที่คำจำกัดความเซิร์ฟเวอร์แรกในไฟล์กำหนดค่าของเราทำหน้าที่เป็น catch-all-server สำหรับการเชื่อมต่อ SSL เราทุกคนรู้ว่ามันเป็นเช่นนั้น (ตรงข้ามกับ http และใช้ default_server config ซึ่งทำงานได้ดี)

สิ่งนี้ไม่สามารถทำได้อย่างชัดเจนสำหรับ SSL (ยัง) ดังนั้นเราต้องใช้รหัสด้วย IF ...

ตัวแปร$hostคือชื่อโฮสต์จากบรรทัดคำขอหรือส่วนหัว http ตัวแปร$server_nameคือชื่อของบล็อกเซิร์ฟเวอร์ที่เราอยู่ในขณะนี้

ดังนั้นหากทั้งสองนี้ไม่เท่ากันคุณจะต้องเสิร์ฟเซิร์ฟเวอร์บล็อก SSL นี้สำหรับโฮสต์อื่นดังนั้นจึงควรถูกบล็อก

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

ตัวอย่าง:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ###
    ### Section: SSL

    #
    ## Check if this certificate is really served for this server_name
    ##   http://serverfault.com/questions/578648/properly-setting-up-a-default-nginx-server-for-https
    if ($host != $server_name) {
        #return 404 "this is an invalid request";
        return       444;
    }

    ...

คุณสามารถอธิบายสิ่งที่listen [::]:443 ssl http2;ทำ? ฉันมีปัญหาในการค้นหาเอกสารประกอบ
แอนดรูว์บราวน์

ฉันคิดว่าฉันพบมันแค่ต้องการรู้ว่าจะค้นหาอะไร คำสั่ง IPV4 และ IPV6
Andrew Brown เมื่อ

7

หากต้องการรายละเอียดเพิ่มเติมเกี่ยวกับคำตอบของ Radmilla Mustafa:

Nginx ใช้ส่วนหัว 'โฮสต์' สำหรับการจับคู่ server_name ไม่ใช้ TLS SNI ซึ่งหมายความว่าสำหรับเซิร์ฟเวอร์ SSL นั้น nginx จะต้องสามารถยอมรับการเชื่อมต่อ SSL ซึ่งจะทำให้มีใบรับรอง / คีย์ ใบรับรอง / คีย์สามารถเป็นได้เช่นลงชื่อด้วยตนเอง

ดูเอกสารประกอบ

ดังนั้นการแก้ปัญหาคือ:

server {
    server_name _;
    listen 80 default_server;
    listen 443 ssl default_server;

    ## To also support IPv6, uncomment this block
    # listen [::]:80 default_server;
    # listen [::]:443 ssl default_server;

    ssl_certificate <path to cert>;
    ssl_certificate_key <path to key>;
    return 404; # or whatever
}

ฉันรู้สึกว่านี่ควรเป็นคำตอบที่ยอมรับได้ ขอบคุณมาก.
รวม

2

สำหรับทุกคนที่สูญเสียเส้นผมมากกว่าฉันอย่างนี้ (ใช้เวลาเกือบทั้งวันในวันนี้) ฉันได้ลองเกือบทุกอย่างแล้วและสิ่งที่ทำให้มันทำงานได้อย่างถูกต้องในที่สุดก็คือบรรทัดที่โง่เง่านี้:

ssl_session_tickets off;

จากคำตอบของIfnotตัวอย่างการทำงานของฉันคือ:

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    server_name _;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    ssl_session_tickets off;

    return 404;
}

ฉันไม่รู้ว่าทำไมสิ่งนี้จึงจำเป็นหลักการเดียวที่ฉันได้จากสิ่งนี้คือ nginx ประพฤติแปลกมากเมื่อเราไม่ให้สิ่งที่เขาต้องการ


1
คุณเป็นตำนาน
Nizar Blond

1

หากคุณต้องการแน่ใจอย่างแน่นอนให้ใช้ที่อยู่ IP แยกต่างหากสำหรับโฮสต์ที่ไม่ควรตอบ HTTPS และโฮสต์ที่ควรใช้ วิธีนี้จะช่วยแก้ไขปัญหาการเตือนเบราว์เซอร์ "ใบรับรองไม่ถูกต้อง"

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