没网?没问题。用Air Gap使用Artifactory
几乎所有的开发组织都需要访问JCenter,NuGet Gallery,npmjs.org,Docker Hub等远程公共资源,以下载构建所需的依赖关系。使用Artifactory的一大好处是它作为代理仓库来下载远程资源和缓存工件。 这样,任何开发人员或CI服务器只要在第一次请求了工件,就可以在内部网络上的Artifactory中的远程资源库中进行缓存和直接使用。这是通过Artifactory使用远程资源的常用方法。
然而,金融机构和军事设施等组织却有严格的安全要求,例如配置,暴露在互联网上的行为是被禁止的。
用Air gap来拯救
为了适应这些案例,我们推荐一个至少有两个Artifactory实例的配置; 一个在DMZ上,另一个在内部网络上,通常称为Air Gap。
我们通常会看到以下两种情况之一:
1、无网络连接
2、单向连接
无网络连接
在这种情况下,两个Artifactory实例之间没有网络连接。 为了从互联网获得依赖关系,外部实例必须下载它们,将它们导出到外部设备(如硬盘驱动器或USB闪存驱动器),然后内部实例必须导入它们供开发人员和CI服务器使用。
获取依赖关系
有两种方法可以从远程仓库获取依赖关系:
1、依赖声明
剥离你的代码,只留下依赖声明。 在具有运行所需工具的DMZ上的虚拟机上安装精简代码(例如,如果您正在开发npm软件包,则需要安装在DMZ机器上的npm客户端)。 相应的客户端通过递归地下载它们的Artifactory来请求所需的依赖关系,以及它们所需要的任何第n级依赖关系。
2、专用脚本
实现一个遍历所有你需要的包的脚本或机制,并向Artifactory发送“头部请求”,以便从远程资源下载这些包。 例如,要从JCenter中获取jQuery的版本2.1.0到2.1.5,可以使用下面的代码片段:
for i in ‘seq 0 5’; do curl -uadmin:password -l http://
localhost:8081/artifactory/jcenter/org/webjars/jquery/
2.1.$i/jquery-2.1.$i.pom
导出和导入
有两种方法可以导出新的依赖关系(即自上次运行导出以来下载的依赖项),然后将其导入到内部文件夹。
1、使用AQL查询的Groovy脚本
编写一个使用AQL查询的Groovy脚本,根据下载日期查找正确的工件。 这样,我们可以确保只导出自上次导出以来下载的那些工件。 例如,要导出2016年8月16日之后创建的所有工件,您可以使用类似以下代码段的东西:
// replace this with your AQL query
def query = 'items.find({"type":"file","created" : {"$gt" : "2016-08-16T19:20:30.45+01:00"},"repo" : "generic-local-archived"})'
// replace this with your Artifactory server
def artifactoryURL = 'http://localhost:8081/artifactory/'
def restClient = new RESTClient(artifactoryURL)
.
.
.
try {
response = restClient.post(path: 'api/search/aql',
body: query,
requestContentType: 'text/plain'
)
} catch (Exception e) {
println(e.message)
}
要下载导出到文件系统或便携式驱动器中的所有工件(到“今天的日期”文件夹中):
public download(RESTClient restClient, List itemsToExport, def dryRun) {
def date = demoFormat()
def folder = "$date"
def dir = "/path/to/export"
def file = new File("$dir/$folder");
file.mkdirs();
dryMessage = (dryRun) ? "*** This is a dry run ***" : "";
itemsToExport.each {
println("Trying to download artifact: '$it'")
try {
if (!dryRun) {
def response = restClient.get(path:String.valueOf(it))
if (response.status == 200) {
String s = it.toString().substring(it.toString().indexOf("/") + 1)
file = new File("$dir/$folder/"+s)
file << response.getData()
println("Artifact '$it' has been successfully downloaded. $dryMessage")
}
else
println("response status: '$response.status'")
}
} catch (HttpResponseException e) {
println("Cannot download artifact '$it': $e.message" +
", $e.statusCode")
}
}
}
现在,我们已经准备好把这个文件夹导入到我们内部的Artifactory实例的选定的仓库中
2、使用JFrog CLI
由于导出基本上是将文件从一个位置复制到另一个位置,因此JFrog CLI是完成该操作的完美工具。 我们要做的是从外部Artifactory实例的“clean repository”中下载所有新的包,然后将它们上传到内部实例。 下载新文件最直接的方法是使用以下命令:
jfrog rt dl generic-local-archived NewFolder/
“但是等一下”你会问 “是不是下载了所有的文件?”看起来像这样,但是由于JFrog CLI是基于checksum的,它只会下载自上次下载以来添加的新的二进制文件。 在底层,JFrog CLI实际上运行一个AQL查询来查找你需要的文件,所以你的回应如下所示:
[Info:] Pinging Artifactory...
[Info:] Done pinging Artifactory.
[Info:] Searching Artifactory using AQL query: items.find({"repo": "generic-local-archived","$or": [{"$and": [{"path": {"$match":"*"},"name":{"$match":"*"}}]}]}).include("name","repo","path","actual_md5","actual_sha1","size")
[Info:] Artifactory response: 200 OK
[Info:] Found 2 artifacts.
[Info:] [Thread 0] Downloading generic-local-archived/jerseywar.tgz
[Info:] [Thread 1] Downloading generic-local-archived/plugin.groovy
[Info:] [Thread 1] Artifactory response: 200 OK
[Info:] [Thread 0] Artifactory response: 200 OK
[Info:] Downloaded 2 artifacts from Artifactory.
现在,您可以将“NewFolder”添加到您的内部实例,并使用JFrog CLI再次上传其内容:
jfrog rt u NewFolder/ generic-local-archive
而且由于JFrog CLI使用checksum和部署(类似于下载的情况),内部实例中目标上已经存在的二进制文件将不会被部署。 下面的输出显示只有一个新文件是Checksum deploy,apex-0.3.4.tar。
[Info:] Pinging Artifactory...
[Info:] Done pinging Artifactory.
[Info:] [Thread 2] Uploading artifact: http://localhost:8081/artifactory/generic-local-archived/plugin.groovy
[Info:] [Thread 1] Uploading artifact: http://localhost:8081/artifactory/generic-local-archived/jerseywar.tgz
[Info:] [Thread 0] Uploading artifact: http://localhost:8081/artifactory/generic-local-archived/apex-0.3.4.tar
[Info:] [Thread 1] Artifactory response: 201 Created
[Info:] [Thread 2] Artifactory response: 201 Created
[Info:] [Thread 0] Artifactory response: 201 Created (Checksum deploy)
[Info:] Uploaded 3 artifacts to Artifactory.
在文件规范中制定复杂查询的简单方法
但是方式并不总是这么简单。 如果我们不想将所有新文件从外部实例移动到内部实例,而是只有那些具有某种“批准标记”的文件。 这就是AQLs制定复杂查询的能力,打开了一个操作的世界。 通过使用AQL,创建查询非常简单,例如,只需将2016年10月15日之后创建的文件,并使用属性workflow.status = PASSED注释,从我们的通用本地归档存储库迁移到NewFolder库。 由于JFrog CLI可以接受参数作为文件规格,我们在名为newandpassed.JSON的文件中创建以下AQL查询:
{
"files": [
{
"aql": {
"items.find": {
"repo": "generic-local-archived",
"created" : {"$gt" : "2016-10-15"},
"@workflow.status" : "PASSED"
}
},
"target": "NewFolder/"
}
]
}
现在在JFrog CLI执行:
jfrog rt dl --spec NewAndPassed.json
现在我们只需要像以前一样将NewFolder的内容上传到内部实例。
单线链接
一些高度安全的机构要求将互联网与其内部网络分开,但政策略宽松,并且允许单向连接。 在这种情况下,内部Artifactory实例可以通过代理或通过安全的单向HTTP网络连接连接到外部实例。 这种设置打开了内部实例获取依赖关系的其他方法:
使用智能远程仓库
使用拉取复制
使用智能远程仓库
Artifactory中的远程仓库是代理远程资源(如Internet上的公共存储库,如JCenter)的存储库。 一个智能远程仓库就是远程资源实际上是另一个Artifactory实例中的一个仓库。
以下是您可以使用的设置:
DMZ上的外部实例包括:
托管已列入白名单的本地存储库已下载,扫描和批准的工件
代理远程资源的远程仓库,需要从中下载依赖关系
一个虚拟资源库,可以聚合所有其他资源
内部实例包括:
托管本地工件的本地存储库,例如版本和其他批准的本地包
远程存储库 - 实际上是一个智能远程存储库,代理外部实例中的虚拟存储库
一个虚拟资源库,可以聚合所有其他资源
这是如何工作的:
构建工具从内部Artifactory实例的虚拟库中请求依赖关系
如果在内部(在任何本地存储库或远程存储库高速缓存中)找不到依赖关系,那么智能远程存储库将从其外部资源(实际上是外部实例上的虚拟存储库)请求它
外部实例的虚拟存储库尝试从其聚集的本地存储库之一或从其远程存储库高速缓存中提供请求的依赖关系。 如果找不到依赖关系,则远程存储库将从远程资源下载该资源,然后将资源从该远程资源提供回请求的内部实例。
有一个小的设置,你需要记住。 内部实例上的虚拟存储库必须具有“Artifactory请求可以检索远程工件”复选框集。
使用拉取复制
在这种方法中,您可以使用上述任何一种方式将依赖关系下载到外部Artifactory实例(在DMZ中)。 现在,您只需在内部实例中创建一个远程存储库,并根据cron作业从外部实例中的“clean”存储库调用pull复制,以将所有列入白名单的依赖关系提供给内部实例。
关于JFrog Xray的说明
如果您在内部Artifactory实例中使用JFrog Xray,则会遇到类似的问题。 为了扫描索引的工件,它必须从与其连接的各种源中收集关于问题和漏洞的数据。 主要来源于JFrog维护的全球数据库服务器,您需要定期与其进行同步以下载新数据。 当你有一个互联网连接时,数据库会自动同步,但是如果没有互联网连接,你必须通过一个类似于上面描述的手动过程来使用离线模式进行依赖。 基本上,您可以在DMZ中的外部实例上使用JFrog CLI下载Xray数据库更新(Xray将为您生成所需的命令),将下载的文件复制到内部Xray服务器,然后执行本地更新。
为了维护与互联网断开连接的安全环境,组织必须付出代价,但这并不意味着他们不能使用市场上的领先工具。 通过一些工具和脚本,以及一些手动交互,他们可以使用Air Gap来访问JFrog Artifactory(和JFrog Xray),以获得近乎在线的体验,同时在其开发环境中保持严格的安全策略。
原网址:https://www.jfrog.com/blog/using-artifactory-with-an-air-gap/