之前的這篇 < a href="/post/2013/10/maven-repository-amazon-s3">「使用 Amazon S3 作為 Maven 套件倉儲」 文章中,山姆鍋提到使用 Amazon S3 作為套件倉儲的構想與好處,但那篇文章只適用于採用 Maven 作為建構工具的專案。山姆鍋已採用 Gradle 作為「影化身科技」的正式建構工具,所以,需要針對 Gradle 找到適合的方案。

經過一番查訪,最終採用了本文所介紹的方案,雖然不是很理想,但尚可接受。Gradle 將解析相依套件 (resolve dependencies) 跟發佈套件 (upload archives) 視為不同任務,不像 Maven 指定好套件倉儲就可以同時用於解析與發佈。所以,底下就分別說明 Gradle 如何發佈到 Amazon S3 以及使用它來解析相依套件。

發佈建構套件

將建構好的套件發佈到 Amazon S3 仍舊需要第三方支援,Gradle 本身尚未有正式支援。下面是一個簡單 Java 專案的範例 build.gradle 檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'maven'

sourceCompatibility = 1.5
group = "com.eavatar"
version = '1.0'

jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
}
}

configurations {
truedeployerJars
}

dependencies {
truecompile group: 'commons-collections', name: 'commons-collections', version: '3.2'
truetestCompile group: 'junit', name: 'junit', version: '4.+'
truedeployerJars "org.springframework.build.aws:org.springframework.build.aws.maven:3.0.0.RELEASE"
}


repositories {
mavenCentral()
}


artifacts {
truearchives jar
}

uploadArchives {
trueconfigurations { deployerJars }
truedependencies {
truetruedeployerJars "org.springframework.build.aws:org.springframework.build.aws.maven:3.0.0.RELEASE"
true}

truerepositories.mavenDeployer { deployer ->
truetruedef repositoryUrl = 's3://BUCKET/'

truetruedescription += repositoryUrl
truetrueconfiguration = configurations.deployerJars
truetrues3credentials = [userName: 'S3_ACCESS_KEY',
truetruetruetruetruetruetrue passphrase: 'S3_SECRET_KEY']

truetruerepository(url: repositoryUrl) { authentication(s3credentials) }
truetruesnapshotRepository(url: repositoryUrl) { authentication(s3credentials) }
true}

}

其中,S3_ACCESS_KEY 跟 S3_SECRET_KEY 要換成您自己從 Amazon IAM 服務取得的 acess key id 以及 secret access key。BUCKET 則需要根據您的 S3 的 Bucket 名稱跟路徑修改。注意建立的這組 key pair 需要對該 S3 bucket 有寫入的權限。基於安全理由,最好也只對該 bucket 有權限。

有了適當的設置後,就可以執行下列指令來發佈到 Amazon S3:

1
$ gradle uploadArchives

解析相依套件

Gradle 不支援直接以 S3 的協定來解析相依套件,也沒有適合的第三方插件可以使用,最好的情況也只是能從 < a href="https://github.com/literalice/gradle-aws-s3-sync" target="_blank" rel="noopener">S3 下載套件到本地。既然無法直接使用 S3 協定,只好轉個彎來解決問題。山姆鍋的解決方案利用 Amazon S3 可以將 Bucket 當作靜態網站的服務器的特性,讓整個套件倉儲的 bucket 可以 HTTP/HTTPS 協定存取。底下是範例用的 build.gradle 檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'maven'

sourceCompatibility = 1.5
group = "com.eavatar"
version = '1.1'

jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
}
}

dependencies {
truecompile group: 'commons-collections', name: 'commons-collections', version: '3.2'
truetestCompile group: 'junit', name: 'junit', version: '4.+'
truecompile 'com.eavatar:gradle-s3-test:1.1'
}

repositories {
mavenCentral()
truemaven {
truetrueurl 'http://S3_BUCKET/'
true}
}

artifacts {
truearchives jar
}

由於 Amazon S3 當作網頁服務器並無法採用 IAM 的用戶認證,以上的設置需要開放給所有人存取,如果是開源軟體,基本上沒什麼問題; 但對於商業專案,應該是不能被接受的,這也是山姆鍋覺得這個做法不完善的地方。針對這點,山姆鍋建議可以下列方式加強:

  • 限制能夠存取的 IP 位址。
  • 將 Bucket 名稱以及路徑加入雜揍碼(hash)。

限制 IP 位址會造成一些不方便,但如果一定需要確保正確的存取控制,這是目前山姆鍋建議的做法。在找尋方案的過程中,山姆鍋也發現了 < a href="http://www.s3auth.com/" target="_blank" rel="noopener">S3Auth 這個服務可以讓您對存取進行 HTTP Basic 認證,由於不確定第三方服務是否可靠,避開了這個做法。

底下是限制 S3 存取 IP 位址的 policy 設置範例,僅供參考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"Version": "2012-10-17",
"Id": "S3PolicyId1",
"Statement": [
{
"Sid": "IPAllow",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::bucket/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": "192.168.143.0/24"
},
"NotIpAddress": {
"aws:SourceIp": "192.168.143.188/32"
}
}
}
]
}

小結

雖然有些不理想,但相信日後 Gradle 對於 Maven 現有專案的支援更完整後,這些問題應該可以解決,限制 IP 位址的作法則是目前可以接收的折衷方案。