Nach einigen Recherchen und Tests habe ich das selbst gelöst. Meine ultimative Lösung bestand darin, den Header referer
zu verwenden, um den Zugriff auf meinen S3-Bucket zu beschränken. Ich habe eine sicherere und detailliertere Lösung erstellt (siehe unten), aber es kam mit einem Leistungseinbruch, der für meine App nicht funktionierte. Meine App basiert auf dem Anzeigen von Fotos und Videos, und es war nicht in den Karten, sie nicht sofort laden zu können. Obwohl ich denke, dass es eine ausreichende Lösung für die meisten Anwendungsfälle sein könnte. Da meine App nicht sehr empfindlich ist, reicht die referer
Kopfzeile für mich aus. Here is how to use the http header referer
to limit access to a bucket.
Lösung Amazon STS mit:
Zuerst müssen Sie sowohl auf die server das AWS SDK haben und die client. Es Meteor bisher Pakete nicht bis zur Verfügung steht, so habe ich meine eigenen. (Ich werde es in Kürze veröffentlichen und hier einen Link setzen, sobald ich das tue.)
Auf dem Server müssen Sie Anmeldeinformationen verwenden, die die Möglichkeit haben, eine Rolle zu übernehmen. Die zu übernehmende Rolle muss eine Vertrauensbeziehung mit dem Benutzer haben, der die Rolle übernimmt. Article on using IAM. - Article on using credentials with SDK
In der Datei server.js
habe ich eine Meteor-Methode erstellt, die ich vom Client aus aufrufen kann. Es prüft zunächst, ob ein Benutzer angemeldet ist. Wenn dies der Fall ist, überprüft es, ob die aktuellen Anmeldedaten in den nächsten 5 Minuten ablaufen. Wenn dies der Fall ist, erstelle ich neue Anmeldeinformationen und schreibe sie entweder in das Benutzerdokument oder gebe sie als Rückruf zurück. Wenn sie in den nächsten 5 Minuten nicht ablaufen, gebe ich ihre aktuellen Temp-Credentials zurück.
Sie müssenMeteor.bindEnvironment
für den Rückruf verwenden.See docs
Meteor.methods({
'awsKey': function(){
if (Meteor.userId()){
var user = Meteor.userId();
var now = moment(new Date());
var userDoc = Meteor.users.findOne({_id: user});
var expire = moment(userDoc.aws.expiration);
var fiveMinutes = 5 * 60 * 1000;
var fut = new Future();
if(moment.duration(expire.diff(now))._milliseconds < fiveMinutes){
var params = {
RoleArn: 'arn:aws:iam::556754141176:role/RoleToAssume',
RoleSessionName: 'SessionName',
DurationSeconds: 3600 //1 Hour
};
var sts = new AWS.STS();
sts.assumeRole(params, Meteor.bindEnvironment((err, data) => {
if (err){
fut.throw(new Error(err));
}else{
Meteor.users.update({_id: user}, {$set: {aws: {accessKey: data.Credentials.AccessKeyId, secretKey: data.Credentials.SecretAccessKey, sessionToken: data.Credentials.SessionToken, expiration: data.Credentials.Expiration}}});
fut.return(data.Credentials);
}
}));
return fut.wait();
}else{
return userDoc.aws;
}
}
}
}
});
Dann können Sie diese Methode manuell oder in einem setInterval
auf Meteor.startup
aufrufen.
Meteor.setInterval(function(){
if(Meteor.userId()){
Meteor.call('awsKey', function(err, data){
if (err){
console.log(err);
}else{
if(data.accessKey){
Session.set('accessKey', data.accessKey);
Session.set('secretKey', data.secretKey);
Session.set('sessionToken', data.sessionToken);
}else{
Session.set('accessKey', data.AccessKeyId);
Session.set('secretKey', data.SecretAccessKey);
Session.set('sessionToken', data.SessionToken);
}
}
});
}
}, 300000); //5 Minute interval
Auf diese Weise werden nur die Schlüssel in einer Session-Variable vom Rückruf festgelegt. Sie können dies tun, indem Sie das Dokument des Benutzers abfragen, um sie auch zu erhalten.
Dann können Sie diese temporären Anmeldeinformationen verwenden, um eine signierte URL für das Objekt zu erhalten, auf das Sie in Ihrem Bucket zugreifen möchten.
Ich habe dies in einem Template-Helfer durch den Namen in der Vorlage es Objekt übergeben:
{{getAwsUrl imageName}}
Template.templateName.helpers({
'getAwsUrl': function(filename){
var accessKey = Session.get('accessKey');
var secretKey = Session.get('secretKey');
var sessionToken = Session.get('sessionToken');
var filename = filename;
var params = {Bucket: 'bucketName', Key: filename, Expires: 6000};
new AWS.S3({accessKeyId: accessKey, secretAccessKey: secretKey, sessionToken: sessionToken, region: 'us-west-2'}).getSignedUrl('getObject', params, function (err, url) {
if (err) {
console.log("Error:" +err);
}else{
result = url;
}
});
return result;
}
});
das alles gibt es zu, es ist! Ich bin mir sicher, dass dies verfeinert werden kann, um besser zu sein, aber das ist genau das, was ich beim Testen davon wirklich schnell gefunden habe. Wie gesagt, es sollte in den meisten Anwendungsfällen funktionieren. Mein besonderer hat nicht. Aus irgendeinem Grund, wenn Sie versuchten, die visibility: visible|hidden;
auf eine img src
dieser signedURLs zu schalten, würden sie viel länger zum Laden als nur die URL direkt einstellen. Es muss sein, weil Amazon die signierte URL auf ihrer Seite entschlüsseln muss, bevor das Objekt zurückgegeben wird.
Dank Mikkel für die Richtung.
Sind in Ihrer App S3-Bildzugriffe authentifiziert? Wenn dies der Fall ist, können Sie diesem authentifizierten GET Zugriff gewähren und allen öffentlichen Zugriff auf Ihren Bucket verweigern. – Mikkel
Nein, im Moment sind es nur öffentlich verfügbare Images während der Entwicklung, aber das möchte ich nicht in der Produktion. Ich weiß nicht wirklich eine Möglichkeit, Authentifizierung vom Client zu tun, außer den Schlüssel und das Geheimnis in der URL oder in der Kopfzeile, die beide zu sehen sind, zu übergeben. Ich denke, die Übergabe im Header wäre besser als URL, aber keine feste Lösung. – gkrizek
Nein, Sie wollen auf keinen Fall die Schlüssel weitergeben, aber Sie können den Meteor-Server verwenden, um eine HTTP-Authentifizierungsanfrage zu senden (die vom Browser nicht sichtbar ist), um ein Authentifizierungs-Token (ich denke) zu erhalten in dem Bild GET-Anfragen – Mikkel