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

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

ฉันคาดหวังว่าบล็อกเซิร์ฟเวอร์ที่แน่นอนเหมือนกัน (เปลี่ยนเฉพาะ "ฟัง 80 default_server" เป็น "ฟัง 443 default_server" และแทนที่จะแสดงหน้า "ส่งคืน 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;
    }
}

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

answer

ฉันจัดการเพื่อกำหนดค่าโฮสติ้งเฉพาะที่ใช้ร่วมกันบน 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 - สร้างใบรับรองที่ลงนามเอง ทดสอบ และโหลดซ้ำ

คุณจะต้องสร้างใบรับรองที่ลงนามด้วยตนเองใน/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

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

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

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

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

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

สิ่งนี้ไม่สามารถทำได้อย่างเปิดเผยสำหรับ 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;
    }

    ...

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

Nginx ใช้ส่วนหัว 'โฮสต์' สำหรับการจับคู่ชื่อเซิร์ฟเวอร์ ไม่ได้ใช้ 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
}

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

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 ทำงานแปลกมากเมื่อเราไม่ให้สิ่งที่เขาต้องการแก่เขา

หากคุณเพียงต้องการให้แน่ใจว่าไม่มีการส่งคืนข้อมูล คุณสามารถใช้ข้อมูลโค้ดต่อไปนี้โดยไม่มีใบรับรอง:

map "" $empty {
        default "";
}

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

        server_name _;

        ssl_ciphers aNULL;
        ssl_certificate data:$empty;
        ssl_certificate_key data:$empty;

        return 444;
}

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