Menyiapkan server nginx "default" dengan benar untuk https

71

Saya memiliki beberapa server yang berjalan di mesin yang sama, beberapa hanya dengan http, beberapa dengan http dan https. Ada beberapa blok server yang didefinisikan dalam file terpisah yang disertakan dari file konfigurasi utama.

Saya telah menyiapkan server "default" untuk http yang akan melayani "halaman pemeliharaan" generik untuk permintaan yang tidak cocok dengan nama server lain dalam file konfigurasi lainnya. Server default http berfungsi seperti yang diharapkan, ia menggunakan nama_server "_" dan muncul pertama dalam daftar include (karena saya telah mengamati bahwa dalam kasus duplikat nama_server di seluruh server, yang muncul pertama kali digunakan). Ini sangat bagus.

Saya akan mengharapkan blok server yang sama persis (hanya beralih "mendengarkan 80 default_server" untuk "mendengarkan 443 default_server" dan juga bukannya melayani halaman "mengembalikan 444") namun tidak. Sebagai gantinya, tampaknya server https default baru sebenarnya mengambil semua koneksi https yang masuk dan menyebabkannya gagal, meskipun blok server lain memiliki nama server yang lebih tepat untuk permintaan yang masuk. Menghapus server https default baru akan menyebabkan perilaku semi-benar untuk dilanjutkan: semua situs web dengan https akan memuat dengan benar; tetapi situs web tanpa https semua akan dialihkan ke server https pertama di file sertakan (yang menurut dokumen, jika tidak ada "default_server" muncul, maka blok server pertama yang muncul akan "default").

Jadi pertanyaan saya adalah, apa cara yang benar untuk mendefinisikan "server default" di nginx untuk koneksi ssl? Mengapa ketika saya secara eksplisit mengatur "default_server" itu menjadi serakah dan mengambil semua koneksi sedangkan ketika saya secara implisit membiarkan nginx memutuskan "server default" itu berfungsi seperti yang saya harapkan (dengan server yang salah ditetapkan sebagai default dan server nyata lainnya berperilaku dengan benar)?

Inilah "server default" saya. Http berfungsi tanpa merusak server lain. Https memecah server lain dan menghabiskan semuanya.

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

Adakah yang melihat apa yang salah di sini?


sumber

Jawaban:

27

Anda tidak memiliki ssl_certificate atau ssl_certificate_key yang ditentukan di blok https "default" Anda. Meskipun Anda tidak memiliki atau menginginkan kunci nyata untuk skenario default ini, Anda masih perlu mengkonfigurasi satu atau yang lain nginx akan memiliki perilaku yang tidak diinginkan yang Anda gambarkan.

Buat sertifikat yang ditandatangani sendiri dengan Nama Umum * dan tancapkan ke konfigurasi Anda dan itu akan mulai berfungsi seperti yang Anda inginkan.

Perilaku "default" di bawah pengaturan ini adalah browser akan mendapat peringatan bahwa sertifikat tidak dapat dipercaya, jika pengguna menambahkan sertifikat sebagai pengecualian, koneksi akan dijatuhkan oleh nginx dan mereka akan melihat default browser mereka. pesan kesalahan "tidak dapat terhubung".

Radmilla Mustafa
sumber
1
Saya sudah mencoba ini tetapi masih tidak berhasil: Semua permintaan ssl ke IP pergi ke host ssl saya yang lain. Ada lagi yang bisa saya coba?
Michael Härtl
23

Saya berhasil mengonfigurasi hosting khusus bersama di satu IP dengan nginx. HTTP default dan HTTPS melayani 404 untuk domain tidak dikenal yang masuk.

1 - Buat zona default

Karena nginx memuat vhosts dalam urutan ascii, Anda harus membuat 00-defaulttautan file / simbolik ke dalam /etc/nginx/sites-enabled.

2 - Isi zona default

Isi Anda 00-defaultdengan vhosts default. Inilah zona yang saya gunakan:

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 - Buat sertifikat, uji, dan muat ulang yang ditandatangani sendiri

Anda harus membuat sertifikat yang ditandatangani sendiri /etc/nginx/ssl/nginx.crt.

Buat sertifikat bertanda tangan default:

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

Hanya pengingat:

  • Tes konfigurasi nginx sebelum memuat ulang / memulai kembali: nginx -t
  • Muat ulang kenikmatan: sudo service nginx reload

Semoga ini bisa membantu.

Jika tidak
sumber
2
Tidak menyelesaikan peringatan peramban untuk mengunjungi situs yang tidak aman.
gdbj
5
Tidak dapat diselesaikan karena tangkapan-semua harus cocok dengan domain apa pun. Tidak ada SSL yang dapat wildcard semua domain. Bayangkan jika Anda spoofing alamat google.fr di server Anda, Anda akan dapat mengotentikasi server Anda sebagai google.fr. Ini akan menjadi masalah keamanan serius :(
Ifnot
Ya, saya rasa itu masuk akal. Sayangnya di Chrome, Anda dihadapkan pada peringatan menakutkan sebelum melihat halaman 404 yang lebih buruk daripada server hanya menolak lalu lintas. Membuatnya terlihat seperti server salah konfigurasi.
gdbj
Terima kasih banyak. Ini bekerja untuk saya, kecuali bahwa saya harus menambahkan default_serveruntuk mendengarkan 443 dan saya menambahkan alamat IPv6 [::]: 80 dan [::]: 443 dengan default_server.
chmike
16

Kami pada dasarnya ingin menghindari dengan cara apa pun bahwa definisi server pertama dalam file config kami berfungsi sebagai catch-all-server untuk koneksi SSL. Kita semua tahu bahwa ia melakukan itu (berlawanan dengan http dan menggunakan konfigurasi default_server yang berfungsi dengan baik).

Ini tidak dapat dicapai secara deklaratif untuk SSL (belum) jadi kami harus mengkodekannya dengan ...

Variabelnya $hostadalah nama host dari baris permintaan atau header http. Variabelnya $server_nameadalah nama blok server tempat kita berada sekarang.

Jadi jika keduanya tidak sama maka Anda melayani blok server SSL ini untuk host lain sehingga harus diblokir.

Kode tidak mengandung referensi khusus ke alamat IP server Anda sehingga dapat dengan mudah digunakan kembali untuk konfigurasi server lain tanpa modifikasi.

Contoh:

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

    ...
Rolf
sumber
Bisakah Anda menjelaskan apa listen [::]:443 ssl http2;fungsinya? Saya kesulitan menemukan dokumentasi untuk itu.
Andrew Brown
Saya rasa saya menemukannya, hanya perlu tahu apa yang harus dicari. Arahan IPV4 dan IPV6.
Andrew Brown
7

Untuk menguraikan lebih lanjut tentang jawaban Radmilla Mustafa:

Nginx menggunakan header 'Host' untuk pencocokan server_name. Itu tidak menggunakan TLS SNI. Ini berarti bahwa untuk server SSL, nginx harus dapat menerima koneksi SSL, yang bermuara pada memiliki sertifikat / kunci. Sertifikat / kunci dapat berupa apa saja, misalnya ditandatangani sendiri.

Lihat dokumentasi

Oleh karena itu, solusinya adalah:

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
}
andreycpp
sumber
Saya merasa bahwa ini harus menjadi jawaban yang diterima. Terimakasih banyak.
aggregate1166877
2

Untuk siapa saja yang kehilangan rambut sebanyak ini karena saya (menghabiskan hampir sepanjang hari untuk hari ini). Saya telah mencoba hampir semua hal, dan hal yang membuatnya akhirnya berfungsi dengan benar adalah garis bodoh ini:

ssl_session_tickets off;

Berdasarkan jawaban Ifnot , contoh kerja saya adalah:

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

Saya tidak tahu mengapa ini perlu, satu-satunya prinsip yang saya dapatkan dari ini adalah bahwa nginx berperilaku sangat aneh ketika kita tidak memberikan apa yang dia inginkan.

Marek Lisý
sumber
1
kamu adalah legenda.
Nizar Blond
1

Jika Anda ingin benar-benar yakin, maka gunakan alamat IP terpisah untuk host yang tidak boleh menjawab pada HTTPS dan host yang seharusnya. Ini juga menyelesaikan masalah peringatan browser "sertifikat tidak sah".

Michael Hampton
sumber