금주의 실패사례 - Jenkins 에서 다른 계정의 S3로 파일 업로드
Jenkins에서 (다른 AWS account)의 S3로 artifact 배포
Jenkins에서 JavaScript로 된 artifact를 S3에 배포하고자 하였는데, 그 Jenkins의 AWS profile과 업로드 하려는 S3가 서로 다른 계정이라 업로드 후 권한이 없어졌고, 이로 인해 누구도 다운로드 할 수 없는 문제가 발생했다.
목표
- JavaScript는 static한 요소로 서버에서 제공해도 되지만, S3에 배포 후 CloudFront를 통해 CDN으로 제공할 수도 있다. 이에 대한 장점 설명은 생략.
- 위와 같은 구성을 위해서는 조건이 필요하다.
- S3의 파일이 직접 외부에 노출되면 안됨
- CloudFront의 Origin access identity만 접근 허용
- OAI 설정 방법: https://aws.amazon.com/premiumsupport/knowledge-center/cloudfront-access-to-amazon-s3/
- Jenkins에서 S3에 업로드
- Jenkins에는 S3 publisher plugin 설치 필요
- Jenkins system configuration에서 Amazon S3 profiles 설정 필요
- S3의 파일이 직접 외부에 노출되면 안됨
- 제약 사항
- 본인의 Jenkins에는 pipeline을 위한 별도의 AWS계정이 연결되어있음
- Amazon S3 profiles 에 등록되는 AWS계정 A(AAAAAAAAAAAA)
- 배포하고자 하는 S3는 다른 계정
- Bucket(이하 TARGET_BUCKET)이 위치한 AWS 계정 B(BBBBBBBBBBBB)
- 본인의 Jenkins에는 pipeline을 위한 별도의 AWS계정이 연결되어있음
이슈
- 계정 A의 profile로 계정 B에 업로드하기
- 계정 B에 CloudFront와 각종 서비스들이 설정되어 있으므로 계정B의 bucket에 업로드하기로 결정
- 계정 B의 bucket에 permissions > bucket policy를 정의
{ "Sid": "Allow to upload from A", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::AAAAAAAAAAAA:root" ] }, "Action": [ "s3:PutObject" ], "Resource": [ "arn:aws:s3:::TARGET_BUCKET", "arn:aws:s3:::TARGET_BUCKET/*" ] }
- Jenkins에서 Post Build에 Publish artifact to S3 Bucket 설정
- 빌드 후 artifact가 성공적으로 업로드 됨
- 사용 방법은 다음 사이트 참고: http://blog.naver.com/chcjswoda/221069905153
- 그러나,
- TARGET_BUCKET은 계정 B에 위치하기 때문에,
- 업로드 된 object의 permission에는 Access for object owner 와 Access for other AWS accounts 둘 다 비어있음
- 계정 A가 다른 계정 B에 업로드 할 때 ACL을 설정하여, TARGET_OBJECT의 full control을 계정 B에게 이양해야 함(별도의 옵션 필요)
- Full control을 넘기는 방법: https://aws.amazon.com/premiumsupport/knowledge-center/s3-require-object-ownership/
$ aws s3api put-object-acl --bucket awsexamplebucket --key example.jpg --acl bucket-owner-full-control
- 자세한 설명: https://medium.com/artificial-industry/how-to-download-files-that-others-put-in-your-aws-s3-bucket-2269e20ed041
- Jenkins에서는 불가능
- S3 Plugin에서 ACL 기능 미지원(한참 기능 업데이트가 없음): https://issues.jenkins-ci.org/browse/JENKINS-20851
- 단, 본인 계정에 업로드 할 경우, 정상적으로 Access for object owner가 등록됨
- Execute shell과 같은 작업으로 ACL 설정을 하고 싶지만, Amazon S3 profile에 접근 불가
- 복잡하게 profile을 읽어오거나, Plugin을 고치거나…
해결
- S3는 복제(Replication)기능을 제공
- Jenkins에서 계정 A의 임시 bucket으로 업로드
- Access for object owner가 정상 등록
- 계정 A의 bucket에서 계정 B의 TARGET_BUCKET으로 복제(management > replication) 설정
- 이때 “Change object ownership to destination bucket owner(객체 소유자를 대상 버킷 소유자로 변경)” 옵션을 이용해 권한 이양
- https://aws.amazon.com/ko/blogs/korea/amazon-s3-introduces-same-region-replication/
- https://aws.amazon.com/ko/blogs/korea/new-cross-region-replication-for-amazon-s3/
- TARGET_BUCKET에서 정상 서비스 제공
- 완성된 bucket policy
- Bucket policy로 많은 내용을 설명 가능
{ "Version": "2008-10-17", "Id": "1", "Statement": [ { "Sid": "Distribute by CloudFront", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXXXXXX" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::TARGET_BUCKET/*" }, { "Sid": "Allow to replicate from A", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::AAAAAAAAAAAA:root" ] }, "Action": [ "s3:GetBucketVersioning", "s3:PutBucketVersioning", "s3:ReplicateObject", "s3:ReplicateDelete", "s3:ObjectOwnerOverrideToBucketOwner" ], "Resource": [ "arn:aws:s3:::TARGET_BUCKET", "arn:aws:s3:::TARGET_BUCKET/*" ] } ] }
- 참고로, TARGET_BUCKET이 복제를 받기 위해서는 versioning이 활성화 되어 있어야 함
- 그리고 기존 파일은 복제되지 않고, 신규로 추가된 파일부터 복제가 적용됨
- Jenkins에서 계정 A의 임시 bucket으로 업로드
참조
- 파이프라인에서 s3Upload호출: https://stackoverflow.com/questions/42074736/jenkins-pipeline-how-to-upload-artifacts-with-s3-plugin