คุณตั้งค่าออบเจ็กต์เริ่มต้นสำหรับไดเร็กทอรีย่อยสำหรับเว็บไซต์ที่โฮสต์แบบคงที่บน Cloudfront ได้อย่างไร


104

คุณตั้งค่าออบเจ็กต์เริ่มต้นสำหรับไดเรกทอรีย่อยบนเว็บไซต์ที่โฮสต์แบบคงที่บน Cloudfront ได้อย่างไร โดยเฉพาะฉันต้องการที่จะให้บริการเมื่อใดก็ตามที่ผู้ใช้จะขอwww.example.com/subdir/index.html www.example.com/subdirหมายเหตุนี่มีไว้สำหรับการส่งมอบเว็บไซต์แบบคงที่ที่เก็บไว้ในที่เก็บข้อมูล S3 นอกจากนี้ฉันต้องการใช้ข้อมูลประจำตัวการเข้าถึงต้นทางเพื่อ จำกัด การเข้าถึงที่เก็บข้อมูล S3 เฉพาะ Cloudfront เท่านั้น

ตอนนี้ฉันทราบแล้วว่า Cloudfront ทำงานแตกต่างจากรัฐ S3 และ Amazon โดยเฉพาะ :

ลักษณะการทำงานของอ็อบเจ็กต์รูทเริ่มต้นของ CloudFront แตกต่างจากลักษณะการทำงานของเอกสารดัชนี Amazon S3 เมื่อคุณกำหนดค่าที่เก็บข้อมูล Amazon S3 เป็นเว็บไซต์และระบุเอกสารดัชนี Amazon S3 จะส่งคืนเอกสารดัชนีแม้ว่าผู้ใช้จะร้องขอไดเร็กทอรีย่อยในที่เก็บข้อมูล (สำเนาของเอกสารดัชนีต้องปรากฏในทุกไดเรกทอรีย่อย) สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่าบัคเก็ต Amazon S3 เป็นเว็บไซต์และเกี่ยวกับเอกสารดัชนีโปรดดูบทที่ Hosting Websites บน Amazon S3 ในคู่มือสำหรับนักพัฒนา Amazon Simple Storage Service

เป็นเช่นนี้แม้ว่า CloudFront ช่วยให้เราสามารถระบุวัตถุรากค่าเริ่มต้นนี้จะทำงานเฉพาะและไม่ได้สำหรับwww.example.com www.example.com/subdirเพื่อหลีกเลี่ยงปัญหานี้เราสามารถเปลี่ยนชื่อโดเมนต้นทางให้ชี้ไปที่ปลายทางของเว็บไซต์ที่ S3 กำหนด วิธีนี้ใช้งานได้ดีและช่วยให้สามารถระบุอ็อบเจ็กต์รูทได้อย่างสม่ำเสมอ ขออภัยดูเหมือนว่าสิ่งนี้ไม่สามารถใช้ร่วมกับข้อมูลประจำตัวการเข้าถึงต้นทางได้ โดยเฉพาะลิงก์ข้างต้นระบุว่า:

เปลี่ยนเป็นโหมดแก้ไข:

การกระจายเว็บ - คลิกแท็บ Origins คลิกจุดเริ่มต้นที่คุณต้องการแก้ไขแล้วคลิกแก้ไข คุณสามารถสร้างข้อมูลประจำตัวการเข้าถึงต้นทางสำหรับต้นทางที่ Origin Type คือ S3 Origin เท่านั้น

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

  1. เป็นไปได้ไหมที่จะระบุออบเจ็กต์รูทเริ่มต้นสำหรับไดเร็กทอรีย่อยทั้งหมดสำหรับเว็บไซต์ที่โฮสต์แบบคงที่บน Cloudfront

  2. เป็นไปได้ไหมที่จะตั้งค่าข้อมูลประจำตัวการเข้าถึงต้นทางสำหรับเนื้อหาที่ให้บริการจาก Cloudfront โดยที่ต้นทางเป็นปลายทางเว็บไซต์ S3 ไม่ใช่ที่เก็บข้อมูล S3


1
ฉันคิดว่าตอนนี้ใช้ Lambda @ edge ได้แล้วโดยใช้ฟังก์ชันที่เปลี่ยนเส้นทาง URL ทั้งหมดที่ลงท้ายด้วย / to /index.html ฉันจะลองใช้บนเว็บไซต์ของฉันและรายงานผลกลับมาและโพสต์การกำหนดค่าโดยละเอียดเป็นคำตอบ
Cristian Măgherușan-Stanciu

คำตอบ:


2

UPDATE: ดูเหมือนว่าฉันจะพูดไม่ถูก! ดูคำตอบของ JBaczuk ซึ่งควรเป็นคำตอบที่ยอมรับได้ในหัวข้อนี้

น่าเสียดายที่คำตอบสำหรับคำถามของคุณทั้งสองคือไม่

1. เป็นไปได้หรือไม่ที่จะระบุออบเจ็กต์เริ่มต้นสำหรับไดเรกทอรีย่อยทั้งหมดสำหรับเว็บไซต์ที่โฮสต์แบบคงที่บน Cloudfront

ไม่ตามที่ระบุไว้ในเอกสาร AWS CloudFront ...

... หากคุณกำหนดอ็อบเจ็กต์รูทเริ่มต้นคำขอของผู้ใช้ปลายทางสำหรับไดเร็กทอรีย่อยของการแจกจ่ายของคุณจะไม่ส่งคืนอ็อบเจ็กต์รูทเริ่มต้น ตัวอย่างเช่นสมมติว่าindex.htmlเป็นออบเจ็กต์เริ่มต้นของคุณและ CloudFront ได้รับคำขอของผู้ใช้ปลายทางสำหรับไดเร็กทอรีการติดตั้งภายใต้การแจกจ่าย CloudFront ของคุณ:

http://d111111abcdef8.cloudfront.net/install/

CloudFront จะไม่ส่งคืนอ็อบเจ็กต์รูทเริ่มต้นแม้ว่าสำเนาของindex.htmlจะปรากฏในไดเร็กทอรีการติดตั้ง

...

ลักษณะการทำงานของอ็อบเจ็กต์รูทเริ่มต้นของ CloudFront แตกต่างจากลักษณะการทำงานของเอกสารดัชนี Amazon S3 เมื่อคุณกำหนดค่าที่เก็บข้อมูล Amazon S3 เป็นเว็บไซต์และระบุเอกสารดัชนี Amazon S3 จะส่งคืนเอกสารดัชนีแม้ว่าผู้ใช้จะร้องขอไดเร็กทอรีย่อยในที่เก็บข้อมูล (สำเนาเอกสารดัชนีต้องปรากฏในทุกไดเรกทอรีย่อย)

2. เป็นไปได้ไหมที่จะตั้งค่าข้อมูลประจำตัวการเข้าถึงต้นทางสำหรับเนื้อหาที่ให้บริการจาก Cloudfront โดยที่ต้นทางเป็นปลายทางของเว็บไซต์ S3 ไม่ใช่ที่เก็บข้อมูล S3

ไม่โดยตรง ตัวเลือกของคุณสำหรับต้นกำเนิดด้วย CloudFront คือบัคเก็ต S3 หรือเซิร์ฟเวอร์ของคุณเอง

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

เมื่อมีการร้องขอมาในการhttp://d111111abcdef8.cloudfront.net/install/ , CloudFront /installจะส่งคำขอนี้ไปยังเซิร์ฟเวอร์ต้นทางขอ คุณสามารถกำหนดค่าเซิร์ฟเวอร์ต้นทางของคุณได้ตามที่คุณต้องการรวมถึงเพื่อให้บริการindex.htmlในกรณีนี้

หรือคุณอาจเขียนเว็บแอปเล็ก ๆ ที่รับสายนี้และรับโดยตรงจาก S3 ก็ได้

แต่ฉันตระหนักดีว่าการตั้งค่าเซิร์ฟเวอร์ของคุณเองและกังวลเกี่ยวกับการปรับขนาดอาจทำให้จุดประสงค์ของสิ่งที่คุณพยายามทำในตอนแรกล้มเหลว


ปัญหาหนึ่งที่ฉันพบคือการทำให้สิ่งนี้ใช้งานได้หมายความว่าคุณจะมี URL สอง (2) ที่สามารถเข้าถึงเว็บไซต์ของคุณบน s3 ได้ URL ด้านหน้าของระบบคลาวด์และ s3 url ของคุณ (bucket_name.s3-website-us-east-1.amazonaws.com)
Hayden

226

มีเป็นวิธีการที่จะทำเช่นนี้ แทนที่จะชี้ไปที่ที่เก็บข้อมูลของคุณโดยเลือกในเมนูแบบเลื่อนลง (www.example.com.s3.amazonaws.com) ให้ชี้ไปที่โดเมนคงที่ของที่เก็บข้อมูลของคุณ (เช่น www.example.com.s3-website-us -west-2.amazonaws.com):

ป้อนคำอธิบายภาพที่นี่

ขอบคุณเธรด AWS Forum นี้


6
มีใครทราบบ้างว่าค่าใช้จ่ายนี้แตกต่างกันเมื่อมีต้นกำเนิด s3 เทียบกับแหล่งที่มาของเว็บ
fideloper

3
วิธีนี้ใช้ได้ดีหรือไม่หากฉันต้องการให้บริการทั้งเว็บไซต์และไฟล์ของฉันHTTPSเท่านั้น
Manjit Kumar

3
หมายความว่าต้องเปิดใช้ S3 เป็นเว็บเซิร์ฟเวอร์หรือไม่?
Anthony Kong

6
OP ระบุอย่างชัดเจนว่าวิธีนี้จะไม่ได้ผลสำหรับเขา: "เพื่อหลีกเลี่ยงปัญหานี้เราสามารถเปลี่ยนชื่อโดเมนต้นทางให้ชี้ไปยังจุดสิ้นสุดของเว็บไซต์ที่ S3 กำหนดได้ซึ่งใช้งานได้ดีและทำให้สามารถระบุออบเจ็กต์รากได้อย่างสม่ำเสมอ แต่น่าเสียดาย ดูเหมือนว่าจะเข้ากันไม่ได้กับข้อมูลประจำตัวการเข้าถึงต้นทาง " AWS เองดูเหมือนจะแนะนำ lamda @ edge สำหรับสิ่งนี้ - aws.amazon.com/blogs/compute/…
icyitscold

4
สิ่งนี้เข้ากันไม่ได้กับ Cloud Front - Origin Access Identity คุณจะไม่สามารถ จำกัด การเข้าถึงที่เก็บข้อมูล S3 ของคุณด้วยวิธีนี้
rocketspacer

16

การเปิดใช้งานโฮสติ้ง S3 หมายความว่าคุณต้องเปิดถังสู่โลก ในกรณีของฉันฉันต้องเก็บที่เก็บข้อมูลไว้เป็นส่วนตัวและใช้ฟังก์ชันการเข้าถึงข้อมูลประจำตัวต้นทางเพื่อ จำกัด การเข้าถึงเฉพาะ Cloudfront เท่านั้น เช่นเดียวกับที่ @Juissi แนะนำฟังก์ชัน Lambda สามารถแก้ไขการเปลี่ยนเส้นทาง:

'use strict';

/**
 * Redirects URLs to default document. Examples:
 *
 * /blog            -> /blog/index.html
 * /blog/july/      -> /blog/july/index.html
 * /blog/header.png -> /blog/header.png
 *
 */

let defaultDocument = 'index.html';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if(request.uri != "/") {
        let paths = request.uri.split('/');
        let lastPath = paths[paths.length - 1];
        let isFile = lastPath.split('.').length > 1;

        if(!isFile) {
            if(lastPath != "") {
                request.uri += "/";
            }

            request.uri += defaultDocument;
        }

        console.log(request.uri);
    }

    callback(null, request);
};

หลังจากคุณเผยแพร่ฟังก์ชันของคุณแล้วให้ไปที่การแจกจ่ายแบบคลาวด์ฟรอนต์ในคอนโซล AWS ไปที่Behaviorsจากนั้นเลือกด้านOrigin Requestล่างLambda Function Associationsและสุดท้ายวาง ARN ลงในฟังก์ชันใหม่ของคุณ


5
มีฟังก์ชั่นแลมบ์ดาที่พร้อมใช้งานเช่นเดียวกับที่: serverlessrepo.aws.amazon.com/applications/…
marcanuy

ปัญหาคือต้องใช้ฟังก์ชันนี้กับ us-east-1 ดังนั้นหากคุณมี บริษัท ภายใต้กฎระเบียบ GDPR ที่เข้มงวดซึ่งไม่อนุญาตให้ใช้งานนอกประเทศเยอรมนีแม้แต่บิตเดียวนี่ก็ไม่เหมาะสำหรับคุณ
Renato Gama

6

มีอีกวิธีหนึ่งในการรับไฟล์เริ่มต้นที่แสดงในไดเร็กทอรีย่อยเช่นexample.com/subdir/. จริงๆแล้วคุณสามารถจัดเก็บไฟล์โดยใช้คีย์subdir/ในที่เก็บข้อมูล(โดยใช้โปรแกรม) ไฟล์นี้จะไม่ปรากฏในคอนโซลการจัดการ S3 แต่มีอยู่จริงและ CloudFront จะให้บริการ


S3 แปลง subdir / เป็น subdir; เมื่อคุณพยายามอัปโหลด HTML นอกจากนี้เมื่อคุณพยายามเข้าถึง example.com/subdir/ ก็ล้มเหลวและหากคุณพยายามเข้าถึง example.com/subdir มันดาวน์โหลดไฟล์ HTML แทนการแสดงผล
jacobfogg

นี่เป็นวิธีที่ดีที่สุดวิธีหนึ่งที่ฉันพบเนื่องจากใช้ได้กับการแจกแจง S3 ทั้งหมดโดยไม่มีการกำหนดค่าเฉพาะและไม่จำเป็นต้องใช้ lambda @ edge (ซึ่งสร้างการเปลี่ยนเส้นทางเพิ่มเติมและทำให้การแสดงหน้าเว็บช้าลงในขณะที่แลมด้าถูกเรียกใช้งาน) ฉันไม่เห็นด้วยกับความคิดเห็นของ @jacobfogg ทำงานได้ดีอย่างสมบูรณ์เมื่อใช้งานโดยใช้โปรแกรม ฉันทำฟังก์ชันแลมบ์ดาขนาดเล็กที่เรียกโดยเหตุการณ์ S3 ดูคำตอบของฉันด้านล่าง
Jeremie

4

วิธีแก้ปัญหาสำหรับปัญหาคือการใช้ lambda @ edge เพื่อเขียนคำขอใหม่ หนึ่งเพียงแค่ต้องตั้งค่าแลมบ์ดาสำหรับเหตุการณ์คำขอของผู้ดูการแจกจ่าย CloudFront และเขียนทุกอย่างที่ลงท้ายด้วย '/' AND ไม่เท่ากับ '/' ด้วยเอกสารรูทเริ่มต้นเช่น index.html


รายละเอียดเพิ่มเติมเกี่ยวกับแนวทางนี้ที่นี่: aws.amazon.com/blogs/compute/…
Henrik Aasted Sørensen

น่าเสียดายที่ Lambda @ Edge ใช้งานได้เฉพาะในภูมิภาค us-east-1 เท่านั้นที่มา: github.com/awslabs/serverless-application-model/issues/635
mruanova

4

มีคำแนะนำ "อย่างเป็นทางการ" ที่เผยแพร่บนบล็อก AWSซึ่งแนะนำให้ตั้งค่าฟังก์ชัน Lambda @ Edge ที่เรียกใช้โดยการแจกจ่าย CloudFront ของคุณ:

แน่นอนว่าเป็นประสบการณ์ที่ไม่ดีของผู้ใช้ที่จะคาดหวังให้ผู้ใช้พิมพ์ index.html ต่อท้ายทุก URL (หรือแม้กระทั่งรู้ว่าควรจะอยู่ที่นั่น) จนถึงขณะนี้ยังไม่มีวิธีง่ายๆในการจัดหา URL ที่ง่ายกว่านี้ (เทียบเท่ากับ DirectoryIndex Directive ในการกำหนดค่า Apache Web Server) ให้กับผู้ใช้ผ่าน CloudFront ไม่ใช่ถ้าคุณยังต้องการ จำกัด การเข้าถึงต้นทาง S3 โดยใช้ OAI อย่างไรก็ตามด้วยการเปิดตัว Lambda @ Edge คุณสามารถใช้ฟังก์ชัน JavaScript ที่ทำงานบนโหนดขอบ CloudFront เพื่อค้นหารูปแบบเหล่านี้และขอคีย์ออบเจ็กต์ที่เหมาะสมจากต้นทาง S3

วิธีการแก้

ในตัวอย่างนี้คุณใช้พลังประมวลผลที่ขอบ CloudFront เพื่อตรวจสอบคำขอเมื่อมาจากไคลเอนต์ จากนั้นเขียนคำขออีกครั้งเพื่อให้ CloudFront ร้องขอวัตถุดัชนีเริ่มต้น (ในกรณีนี้) สำหรับ URI คำขอใด ๆ ที่ลงท้ายด้วย '/'

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

'use strict';
exports.handler = (event, context, callback) => {

    // Extract the request from the CloudFront event that is sent to Lambda@Edge
    var request = event.Records[0].cf.request;

    // Extract the URI from the request
    var olduri = request.uri;

    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    var newuri = olduri.replace(/\/$/, '\/index.html');

    // Log the URI as received by CloudFront and the new URI to be used to fetch from origin
    console.log("Old URI: " + olduri);
    console.log("New URI: " + newuri);

    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;

    // Return to CloudFront
    return callback(null, request);

};

ทำตามคำแนะนำที่ลิงก์ด้านบนเพื่อดูขั้นตอนทั้งหมดที่จำเป็นในการตั้งค่านี้รวมถึงบัคเก็ต S3 การแจกจ่าย CloudFront และการสร้างฟังก์ชันLambda @ Edge


2

อีกทางเลือกหนึ่งในการใช้ lambda @ edge คือการใช้หน้าข้อผิดพลาดของ CloudFront ตั้งค่าการตอบสนองข้อผิดพลาดที่กำหนดเองเพื่อส่ง 403 ทั้งหมดไปยังไฟล์เฉพาะ จากนั้นเพิ่ม javascript ลงในไฟล์นั้นเพื่อต่อท้าย index.html เข้ากับ url ที่ลงท้ายด้วย a / โค้ดตัวอย่าง:

if ((window.location.href.endsWith("/") && !window.location.href.endsWith(".com/"))) {
    window.location.href = window.location.href + "index.html";
}
else {
    document.write("<Your 403 error message here>");
}

1

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันเพิ่งผ่านปัญหานี้มาด้วยตัวเอง ในที่สุดเป้าหมายของฉันก็คือการตั้งค่าไฟล์เริ่มต้นในไดเร็กทอรีน้อยลงและอื่น ๆ อีกมากมายเพื่อให้ได้ผลลัพธ์สุดท้ายของไฟล์ที่ถูกแสดงโดยไม่มี.htmlจุดสิ้นสุดของไฟล์

ฉันลงเอยด้วยการลบออก.htmlจากชื่อไฟล์และตั้งค่าประเภท mime เป็นโปรแกรม / ด้วยตนเองเป็นtext/html. ไม่ใช่วิธีดั้งเดิม แต่ดูเหมือนจะได้ผลและตอบสนองความต้องการของฉันสำหรับ URL ที่สวยงามโดยไม่ต้องเสียประโยชน์ของ cloudformation การตั้งค่าประเภทละครใบ้เป็นเรื่องที่น่ารำคาญ แต่ราคาเพียงเล็กน้อยสำหรับผลประโยชน์ในความคิดของฉัน


0

@ johan-gorter ระบุไว้ข้างต้นว่า CloudFront ให้บริการไฟล์ที่มีคีย์ที่ลงท้ายด้วย / หลังจากการตรวจสอบดูเหมือนว่าตัวเลือกนี้ใช้งานได้และสามารถสร้างไฟล์ประเภทนี้ใน S3 โดยทางโปรแกรมได้ ดังนั้นฉันจึงเขียนแลมบ์ดาขนาดเล็กที่ถูกทริกเกอร์เมื่อไฟล์ถูกสร้างขึ้นบน S3 โดยมีคำต่อท้าย index.html หรือ index.htm

สิ่งที่ทำคือการคัดลอกวัตถุdir/subdir/index.htmlลงในวัตถุdir/subdir/

import json
import boto3

s3_client = boto3.client("s3")

def lambda_handler(event, context):

    for f in event['Records']:

        bucket_name = f['s3']['bucket']['name']
        key_name = f['s3']['object']['key']
        source_object = {'Bucket': bucket_name, 'Key': key_name}

        file_key_name = False

        if key_name[-10:].lower() == "index.html" and key_name.lower() != "index.html":
            file_key_name = key_name[0:-10]
        elif key_name[-9:].lower() == "index.htm" and key_name.lower() != "index.htm":
            file_key_name = key_name[0:-9]
        
        if file_key_name:
            s3_client.copy_object(CopySource=source_object, Bucket=bucket_name, Key=file_key_name)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.