Como você define um objeto raiz padrão para subdiretórios de um site hospedado estaticamente no Cloudfront?

96

Como você define um objeto raiz padrão para subdiretórios em um site hospedado estaticamente no Cloudfront? Especificamente, gostaria www.example.com/subdir/index.htmlde ser atendido sempre que o usuário solicitar www.example.com/subdir. Observe, isso é para entregar um site estático mantido em um balde S3. Além disso, gostaria de usar uma identidade de acesso de origem para restringir o acesso ao bucket S3 apenas para o Cloudfront.

Agora, estou ciente de que o Cloudfront funciona de maneira diferente do S3 e dos estados amazônicos, especificamente :

O comportamento dos objetos raiz padrão do CloudFront é diferente do comportamento dos documentos de índice do Amazon S3. Quando você configura um bucket do Amazon S3 como um site e especifica o documento de índice, o Amazon S3 retorna o documento de índice mesmo se um usuário solicitar um subdiretório no bucket. (Uma cópia do documento de índice deve aparecer em cada subdiretório.) Para obter mais informações sobre a configuração de buckets do Amazon S3 como sites e sobre documentos de índice, consulte o capítulo Hosting Websites no Amazon S3 no Guia do desenvolvedor do Amazon Simple Storage Service.

Assim, embora o Cloudfront nos permita especificar um objeto raiz padrão, isso só funciona para www.example.come não para www.example.com/subdir. Para contornar essa dificuldade, podemos alterar o nome de domínio de origem para apontar para o endpoint do site fornecido por S3. Isso funciona muito bem e permite que os objetos raiz sejam especificados uniformemente. Infelizmente, isso não parece ser compatível com as identidades de acesso de origem . Especificamente, os links acima afirmam:

Mude para o modo de edição:

Distribuições da Web - Clique na guia Origens, clique na origem que deseja editar e clique em Editar. Você só pode criar uma identidade de acesso de origem para origens cujo Tipo de Origem seja Origem S3.

Basicamente, para definir o objeto raiz padrão correto, usamos o endpoint do site S3 e não o bucket do site em si. Isso não é compatível com o uso de identidade de acesso de origem. Como tal, minhas perguntas se resumem a

  1. É possível especificar um objeto raiz padrão para todos os subdiretórios de um site hospedado estaticamente no Cloudfront?

  2. É possível configurar uma identidade de acesso de origem para conteúdo servido de Cloudfront onde a origem é um endpoint de site S3 e não um bucket S3?

wyer33
fonte
1
Acho que agora é possível fazer isso com Lambda @ edge, usando uma função que redireciona todos os URLs que terminam em / para /index.html. Vou experimentar em meu site e relatar os resultados e postar a configuração detalhada como uma resposta.
Cristian Măgherușan-Stanciu

Respostas:

1

ATUALIZAÇÃO: Parece que eu estava incorreto! Veja a resposta de JBaczuk, que deve ser a resposta aceita neste tópico.

Infelizmente, a resposta a ambas as perguntas é não.

1. É possível especificar um objeto raiz padrão para todos os subdiretórios de um site hospedado estaticamente no Cloudfront?

Não. Conforme declarado nos documentos do AWS CloudFront ...

... Se você definir um objeto raiz padrão, uma solicitação do usuário final para um subdiretório de sua distribuição não retorna o objeto raiz padrão. Por exemplo, suponha que index.htmlseja o seu objeto raiz padrão e que o CloudFront receba uma solicitação do usuário final para o diretório de instalação em sua distribuição do CloudFront:

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

O CloudFront não retornará o objeto raiz padrão, mesmo se uma cópia de index.htmlaparecer no diretório de instalação.

...

O comportamento dos objetos raiz padrão do CloudFront é diferente do comportamento dos documentos de índice do Amazon S3. Quando você configura um bucket do Amazon S3 como um site e especifica o documento de índice, o Amazon S3 retorna o documento de índice mesmo se um usuário solicitar um subdiretório no bucket. (Uma cópia do documento de índice deve aparecer em cada subdiretório.)

2. É possível configurar uma identidade de acesso de origem para conteúdo servido de Cloudfront onde a origem é um endpoint de site S3 e não um bucket S3?

Não diretamente. Suas opções de origens com CloudFront são baldes S3 ou seu próprio servidor.

É essa segunda opção que abre algumas possibilidades interessantes, no entanto. Isso provavelmente vai contra o propósito do que você está tentando fazer, mas você pode configurar seu próprio servidor, cuja única função é ser um servidor de origem do CloudFront.

Quando chega uma solicitação para http://d111111abcdef8.cloudfront.net/install/ , o CloudFront encaminhará essa solicitação para seu servidor de origem, solicitando /install. Você pode configurar seu servidor de origem como quiser, inclusive para servir index.htmlneste caso.

Ou você pode escrever um pequeno aplicativo da web que apenas atenda esta chamada e a obtenha diretamente do S3 de qualquer maneira.

Mas eu percebo que configurar seu próprio servidor e se preocupar com dimensionamento pode anular o propósito do que você está tentando fazer em primeiro lugar.

Josh Padnick
fonte
O único problema que tenho com isso é que fazer isso funcionar significa que você teria dois (2) URLs capazes de acessar seu site no s3. Seu URL frontal da nuvem e seu URL s3 (bucket_name.s3-website-us-east-1.amazonaws.com)
Hayden
218

Não é uma maneira de fazer isso. Em vez de apontá-lo para seu intervalo, selecionando-o no menu suspenso (www.example.com.s3.amazonaws.com), aponte-o para o domínio estático de seu intervalo (por exemplo, www.example.com.s3-website-us -west-2.amazonaws.com):

insira a descrição da imagem aqui

Graças a este tópico do fórum AWS

JBaczuk
fonte
6
Alguém sabe se isso cobra diferente quando se tem uma origem s3 vs uma origem web?
fideloper
3
Isso funciona bem se eu quiser veicular todo o meu site e arquivos HTTPSapenas?
Manjit Kumar
3
Isso significa que o S3 deve ser habilitado como servidor web?
Anthony Kong
6
OP declarou explicitamente que essa abordagem não funcionará para ele: "Para contornar essa dificuldade, podemos alterar o nome de domínio de origem para apontar para o endpoint do site fornecido por S3. Isso funciona muito bem e permite que os objetos raiz sejam especificados de maneira uniforme. Infelizmente , isso não parece ser compatível com as identidades de acesso de origem ". A AWS parece estar recomendando lamda @ edge para isso - aws.amazon.com/blogs/compute/…
icyitscold
3
Isso não é compatível com o Cloud Front - Origin Access Identity. Você não poderá restringir o acesso ao seu bucket S3 desta forma.
rocketspacer de
15

Ativar a hospedagem S3 significa que você precisa abrir o balde para o mundo. No meu caso, eu precisava manter o intervalo privado e usar a funcionalidade de identidade de acesso de origem para restringir o acesso apenas ao Cloudfront. Como @Juissi sugeriu, uma função Lambda pode corrigir os redirecionamentos:

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

Depois de publicar sua função, vá para a distribuição do cloudfront no console da AWS. Vá para Behaviors, escolha Origin Requestem Lambda Function Associationse, por fim, cole o ARN em sua nova função.

Kenske
fonte
5
Há uma função lambda pronta para implantar semelhante a esta: serverlessrepo.aws.amazon.com/applications/…
marcanuy
O problema aqui é que essa função precisa ser implantada em us-east-1, portanto, se você tem uma empresa sob estrita regulamentação GDPR que não permite um único bit fora da Alemanha, isso não é para você.
Renato Gama
5

Existe uma outra maneira de obter um arquivo padrão servido em um subdiretório, como example.com/subdir/. Na verdade, você pode (programaticamente) armazenar um arquivo com a chave subdir/no intervalo. Este arquivo não aparecerá no console de gerenciamento S3, mas ele realmente existe e o CloudFront irá atendê-lo.

Johan Gorter
fonte
S3 converst subdir / to subdir; quando você tenta fazer o upload do HTML. Além disso, quando você tenta acessar example.com/subdir/, ele falha, e se você tentar acessar example.com/subdir; ele baixa o arquivo HTML em vez de renderizá-lo.
jacobfogg
4

A solução alternativa para o problema é utilizar lambda @ edge para reescrever as solicitações. Basta configurar o lambda para o evento de solicitação do visualizador da distribuição do CloudFront e reescrever tudo que termina com '/' E não é igual a '/' com o documento raiz padrão, por exemplo, index.html.

Juissi
fonte
Mais detalhes sobre essa abordagem aqui: aws.amazon.com/blogs/compute/…
Henrik Aasted Sørensen
infelizmente, Lambda @ Edge só funciona na região us-east-1, fonte: github.com/awslabs/serverless-application-model/issues/635
mruanova
4

Há um guia "oficial" publicado no blog da AWS que recomenda a configuração de uma função Lambda @ Edge acionada por sua distribuição do CloudFront:

Obviamente, é uma experiência ruim para o usuário esperar que os usuários sempre digitem index.html no final de cada URL (ou mesmo saibam que ele deveria estar lá). Até agora, não havia uma maneira fácil de fornecer esses URLs mais simples (equivalente à Diretiva DirectoryIndex em uma configuração do Apache Web Server) aos usuários por meio do CloudFront. Não se você ainda quiser restringir o acesso à origem S3 usando um OAI. No entanto, com o lançamento do Lambda @ Edge, você pode usar uma função JavaScript em execução nos nós de borda do CloudFront para procurar esses padrões e solicitar a chave de objeto apropriada da origem S3.

Solução

Neste exemplo, você usa o poder de computação na borda do CloudFront para inspecionar a solicitação conforme ela chega do cliente. Em seguida, reescreva a solicitação para que o CloudFront solicite um objeto de índice padrão (index.html neste caso) para qualquer URI de solicitação que termina em '/'.

Quando uma solicitação é feita em um servidor web, o cliente especifica o objeto a ser obtido na solicitação. Você pode usar este URI e aplicar uma expressão regular a ele para que esses URIs sejam resolvidos para um objeto de índice padrão antes do CloudFront solicitar o objeto da origem. Use o seguinte código:

'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);

};

Siga o guia vinculado acima para ver todas as etapas necessárias para configurar isso, incluindo bucket S3, distribuição do CloudFront e criação de função Lambda @ Edge .

Max Desiatov
fonte
2

Outra alternativa para usar lambda @ edge é usar as páginas de erro do CloudFront. Configure uma resposta de erro personalizada para enviar todos os 403s para um arquivo específico. Em seguida, adicione javascript a esse arquivo para anexar index.html a urls que terminam em /. Código de amostra:

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>");
}
user1333371
fonte
1

Eu sei que essa é uma pergunta antiga, mas eu mesma lutei contra isso. Em última análise, meu objetivo era menos definir um arquivo padrão em um diretório e mais ter o resultado final de um arquivo que foi servido sem .htmlno final dele

Acabei removendo .htmldo nome do arquivo e programaticamente / manualmente defini o tipo MIME para text/html. Não é a maneira tradicional, mas parece funcionar e satisfaz meus requisitos para os urls bonitos sem sacrificar os benefícios do cloudformation. Definir o tipo MIME é irritante, mas um pequeno preço a pagar pelos benefícios, na minha opinião

whtevn
fonte