《架构师》2016年6月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

使用S3的典型场景

S3的一些典型使用场景如下:

●存储用户上传的文件,如头像,照片,视频等静态内容。

●当作一个的key value store,承担简单的数据库服务功能。

●数据备份。

●静态网站的托管:你可以对一个bucket使能Web Hosting。

我们简单介绍一下S3实现静态网站托管,然后以一个例子讲述如何使用S3实现一个能最大程度保证数据安全同时又价格低廉的团队内部的文件服务器。

使用S3 实现安全的静态网站托管

经常使用GitHub的朋友对GitHub pages服务一定不会陌生,你只要把各种静态网站生成工具的生成的目标放入gh-pages的branch, GitHub pages就会帮你做静态网站的托管。得益于如今越来越强大的JavaScript和各种API,静态网站其实早已脱离了展示HTML的基本范畴。

GitHub pages有一个缺点就是,只要你使用,它就是开放的,无法变成一个私有网站,存放公司内部的私密文件。公司内部的一些私有内容,比如:

●使用reveal.js生成的slides。

●使用new relic生成的各种嵌入式报表和图表。

●使用JavaScript + AWS SDK做的各种内部工具(由于AWS SDK提供了JavaScript SDK,所以你可以用静态网站的方式访问数据库等服务,实现server less的效果)。

你无论如何都不会想将其暴露给外界。这个时候,GitHub pages就不适用,我们可以使用S3 Web Hosting + IAM policy来完成。

使能S3 Web Hosting是件很简单的事情,只需在AWS console中,为对应的bucket打开这个选项即可,然后添加如下IAM policy:

    001 {
    002   “Version”: “2012-10-17”,
    003   “Statement”: [
    004     {
    005       “Sid”: “PublicReadGetObject”,
    006       “Effect”: “Allow”,
    007       “Principal”: “*”,
    008       “Action”: “s3:GetObject”,
    009       “Resource”: “arn:AWS:s3:::team-assets/*”
    010     }
    011   ]
    012 }

S3 Web Hosting会告诉你一个用于访问的域名,你也可将你自己的私有域名指定一个CNAME指向该域。这样配置下来,只要域名和要访问的文件夹没有暴露,文件内容就是安全的。适用于安全等级不高的内容。

如果需要更高的安全级别,可以配合VPC + IAM policy。一般而言,使用VPC的用户,都会将VPC设置成私有网络(比如10.0.0.x的网络),然后在网络边界配置一台VPN服务器,用于内外网的交互。任何用户要访问内网,必须先接入VPN。我们可以设置用于Web Hosting的S3的bucket的IAM仅允许VPN服务器的IP访问,如下:

    001 {
    002   “Version”: “2012-10-17”,
    003   “Statement”: [
    004     {
    005       “Sid”: “PublicReadGetObject”,
    006       “Effect”: “Allow”,
    007       “Principal”: “*”,
    008       “Action”: “s3:GetObject”,
    009       “Resource”: “arn:AWS:s3:::team-assets/*”,
    010       “Condition” :  {
    011         “IpAddress” : {
    012           “aws:SourceIp” : [“5.5.5.5/32”]
    013         }
    014       }
    015     }
    016   ]
    017 }

那么,只用当用户接入VPN之后,才能访问Web Hosting的域名下的文件,进一步提高了安全性。当然,由于不是在路由层面控制访问,所以没办法防止ip spoofing,还是有一些潜在风险的,不过风险不大。(攻击者需要知道要访问的文件所在的域名和路径,并且知道仅允许哪个源IP访问,进而进行IP spoofing,而公网上IP spoofing的难度很大,基本上所有的路由器都会做reverse route check。)

使用S3实现文件服务器

很多公司都会为员工提供私人和共享的文件存储。比如作为一个用户,我可以把我的私人文件存放在:fileserver://home/tyrchen/*下,把一些共享文件存放在fileserver://public/tyrchen/* 下。为了能够安全的存储这些文件,公司的IT部门一般会使用昂贵的SAN(Storage Area Network)来保证一定程度的SLA(Service Level Agreement),同时,还要做各种各样的备份(和恢复)。如果我们使用S3来实现类似的文件服务器,其代价和未来的维护成本会小得多。此外,我们还可以做一些额外的开发,使得文件服务器的使用体验类似于Dropbox。

大致的想法是这样的:

新员工入职后会为其在S3上建立home folder,用来保存重要的私人文件和共享文件。

员工电脑的本地文件中会有一个目录corp-fs-box,里面包含三个子目录:

●private:存放任意文件,私有,会自动sync到私人目录,别人无法访问。

●photos:存放各种媒体文件,公开,会自动sync到共享目录,并生成合适的尺寸放在供Web访问的S3 bucket中。

员工只要在本地目录中存放文件,就会按照上述规则自动同步,类似Dropbox。

解决思路:

创建两个S3 bucket:corp-fs-team和corp-fs-web。corp-fs-web打开Web Hosting功能。

使用IAM policy来设置home folder的权限。

使用aws s3sync来同步文件夹:

●对本地corp-fs-box/private里的文件,同步到S3://corp-fs-team/home/{AWS:username}/ 中。这个目录只有当前用户可以访问,其他用户不能访问。

●对本地corp-fs-box/pub/photos里的文件,同步到S3:web//corp-fs-team/pub/photos中。这个目录任何用户都可以访问并修改。

S3配置Events,使得对于S3://corp-fs-team/pub/photos/{AWS:username}/的任何更新行为(添加/删除)都会触发lambda函数。

lambda函数扫描上传的文件,如果是*.jpg或者*.mp4/*.mov,则将其进行resize/transcoding等处理,并将编译的结果放在S3://corp-fs-web/pub/photos/{AWS:username}/*下,供内网的用户浏览。

涉及的IAM policy如下:

    001 {
    002   “Version”: “2012-10-17”,
    003   “Statement”: [{
    004       “Sid”: “AllowGroupToSeeBucketList”,
    005       “Action”: [“s3:ListAllMyBuckets”, “s3:GetBucketLocation”],
    006       “Effect”: “Allow”,
    007       “Resource”: [“arn:AWS:s3:::*”]
    008     }, {
    009       “Sid”: “AllowRootLevelList”,
    010       “Action”: [“s3:ListBucket”],
    011       “Effect”: “Allow”,
    012       “Resource”: [“arn:AWS:s3:::corp-fs-team”],
    013       “Condition”: {
    014         “StringEquals”: {
    015           “s3:prefix”: [“”, “home/”],
    016           “s3:delimiter”: [“/”]
    017         }
    018       }
    019     }, {
    020       “Sid”: “AllowListForUserPrefix”,
    021       “Action”: [“s3:ListBucket”],
    022       “Effect”: “Allow”,
    023       “Resource”: [“arn:AWS:s3:::corp-fs-team”],
    024       “Condition”: {
    025         “StringLike”: {
    026           “s3:prefix”: [“home/${AWS:username}/*”]
    027         }
    028       }
    029     }, {
    030       “Sid”: “AllowUserFullAccessToUserPrefix”,
    031       “Action”: [“s3:*”],
    032       “Effect”: “Allow”,
    033       “Resource”: [
    034         “arn:AWS:s3:::corp-fs-team/home/${AWS:username}”,
    035         “arn:AWS:s3:::corp-fs-team/home/${AWS:username}/*”
    036       ]
    037     }
    038   ]
    039 }

以及访问pub目录的IAM policy(见下页)。

具体的lambda函数不在本文讨论的范围之内。

除此之外,我们还需要一个类似于Dropbox的客户端软件来监控本地目录(S3目录)的更改,以便在合适的时候进行同步。思路如下:

客户端软件做成一个开机启动的daemon。

随时监控本地目录corp-fs-box/*和S3 bucket的修改,并按上述规则同步。

    001 {
    002   “Version”: “2012-10-17”,
    003   “Statement”: [{
    004       “Sid”: “AllowPublicLevelList”,
    005       “Action”: [“s3:ListBucket”],
    006       “Effect”: “Allow”,
    007       “Resource”: [“arn:AWS:s3:::corp-fs-team-bucket”],
    008       “Condition”: {
    009         “StringLike”: {
    010           “s3:prefix”: [“pub/*”]
    011         }
    012       }
    013     }, {
    014       “Sid”: “AllowUserFullAccessToPublicPrefix”,
    015       “Action”: [“s3:*”],
    016       “Effect”: “Allow”,
    017       “Resource”: [
    018         “arn:AWS:s3:::corp-fs-team-bucket/pub”,
    019         “arn:AWS:s3:::corp-fs-team-bucket/pub/*”
    020       ]
    021     }
    022   ]
    023 }

由于涉及的目录都是个人目录,不太会产生冲突(除非同一用户在多个device下载没有sync的前提下修改同一文件。所以在这里,为简单起见,我们可以不涉及到diff/merge,简单遵循last writer wins进行处理就可以了。另外S3自带versioning,也可以使能这一功能,保存历史版本,在冲突发生的时候,让用户选择。