endpoint.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. package s3
  2. import (
  3. "fmt"
  4. "github.com/aws/aws-sdk-go/aws/awserr"
  5. "github.com/aws/aws-sdk-go/aws/endpoints"
  6. "net/url"
  7. "strings"
  8. "github.com/aws/aws-sdk-go/aws"
  9. awsarn "github.com/aws/aws-sdk-go/aws/arn"
  10. "github.com/aws/aws-sdk-go/aws/request"
  11. "github.com/aws/aws-sdk-go/internal/s3shared"
  12. "github.com/aws/aws-sdk-go/internal/s3shared/arn"
  13. )
  14. const (
  15. s3Namespace = "s3"
  16. s3AccessPointNamespace = "s3-accesspoint"
  17. s3ObjectsLambdaNamespace = "s3-object-lambda"
  18. s3OutpostsNamespace = "s3-outposts"
  19. )
  20. // Used by shapes with members decorated as endpoint ARN.
  21. func parseEndpointARN(v string) (arn.Resource, error) {
  22. return arn.ParseResource(v, accessPointResourceParser)
  23. }
  24. func accessPointResourceParser(a awsarn.ARN) (arn.Resource, error) {
  25. resParts := arn.SplitResource(a.Resource)
  26. switch resParts[0] {
  27. case "accesspoint":
  28. switch a.Service {
  29. case s3Namespace:
  30. return arn.ParseAccessPointResource(a, resParts[1:])
  31. case s3ObjectsLambdaNamespace:
  32. return parseS3ObjectLambdaAccessPointResource(a, resParts)
  33. default:
  34. return arn.AccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: fmt.Sprintf("service is not %s or %s", s3Namespace, s3ObjectsLambdaNamespace)}
  35. }
  36. case "outpost":
  37. if a.Service != "s3-outposts" {
  38. return arn.OutpostAccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: "service is not s3-outposts"}
  39. }
  40. return parseOutpostAccessPointResource(a, resParts[1:])
  41. default:
  42. return nil, arn.InvalidARNError{ARN: a, Reason: "unknown resource type"}
  43. }
  44. }
  45. // parseOutpostAccessPointResource attempts to parse the ARNs resource as an
  46. // outpost access-point resource.
  47. //
  48. // Supported Outpost AccessPoint ARN format:
  49. // - ARN format: arn:{partition}:s3-outposts:{region}:{accountId}:outpost/{outpostId}/accesspoint/{accesspointName}
  50. // - example: arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/accesspoint/myaccesspoint
  51. func parseOutpostAccessPointResource(a awsarn.ARN, resParts []string) (arn.OutpostAccessPointARN, error) {
  52. // outpost accesspoint arn is only valid if service is s3-outposts
  53. if a.Service != "s3-outposts" {
  54. return arn.OutpostAccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: "service is not s3-outposts"}
  55. }
  56. if len(resParts) == 0 {
  57. return arn.OutpostAccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: "outpost resource-id not set"}
  58. }
  59. if len(resParts) < 3 {
  60. return arn.OutpostAccessPointARN{}, arn.InvalidARNError{
  61. ARN: a, Reason: "access-point resource not set in Outpost ARN",
  62. }
  63. }
  64. resID := strings.TrimSpace(resParts[0])
  65. if len(resID) == 0 {
  66. return arn.OutpostAccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: "outpost resource-id not set"}
  67. }
  68. var outpostAccessPointARN = arn.OutpostAccessPointARN{}
  69. switch resParts[1] {
  70. case "accesspoint":
  71. accessPointARN, err := arn.ParseAccessPointResource(a, resParts[2:])
  72. if err != nil {
  73. return arn.OutpostAccessPointARN{}, err
  74. }
  75. // set access-point arn
  76. outpostAccessPointARN.AccessPointARN = accessPointARN
  77. default:
  78. return arn.OutpostAccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: "access-point resource not set in Outpost ARN"}
  79. }
  80. // set outpost id
  81. outpostAccessPointARN.OutpostID = resID
  82. return outpostAccessPointARN, nil
  83. }
  84. func parseS3ObjectLambdaAccessPointResource(a awsarn.ARN, resParts []string) (arn.S3ObjectLambdaAccessPointARN, error) {
  85. if a.Service != s3ObjectsLambdaNamespace {
  86. return arn.S3ObjectLambdaAccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: fmt.Sprintf("service is not %s", s3ObjectsLambdaNamespace)}
  87. }
  88. accessPointARN, err := arn.ParseAccessPointResource(a, resParts[1:])
  89. if err != nil {
  90. return arn.S3ObjectLambdaAccessPointARN{}, err
  91. }
  92. if len(accessPointARN.Region) == 0 {
  93. return arn.S3ObjectLambdaAccessPointARN{}, arn.InvalidARNError{ARN: a, Reason: fmt.Sprintf("%s region not set", s3ObjectsLambdaNamespace)}
  94. }
  95. return arn.S3ObjectLambdaAccessPointARN{
  96. AccessPointARN: accessPointARN,
  97. }, nil
  98. }
  99. func endpointHandler(req *request.Request) {
  100. endpoint, ok := req.Params.(endpointARNGetter)
  101. if !ok || !endpoint.hasEndpointARN() {
  102. updateBucketEndpointFromParams(req)
  103. return
  104. }
  105. resource, err := endpoint.getEndpointARN()
  106. if err != nil {
  107. req.Error = s3shared.NewInvalidARNError(nil, err)
  108. return
  109. }
  110. resReq := s3shared.ResourceRequest{
  111. Resource: resource,
  112. Request: req,
  113. }
  114. if len(resReq.Request.ClientInfo.PartitionID) != 0 && resReq.IsCrossPartition() {
  115. req.Error = s3shared.NewClientPartitionMismatchError(resource,
  116. req.ClientInfo.PartitionID, aws.StringValue(req.Config.Region), nil)
  117. return
  118. }
  119. if !resReq.AllowCrossRegion() && resReq.IsCrossRegion() {
  120. req.Error = s3shared.NewClientRegionMismatchError(resource,
  121. req.ClientInfo.PartitionID, aws.StringValue(req.Config.Region), nil)
  122. return
  123. }
  124. switch tv := resource.(type) {
  125. case arn.AccessPointARN:
  126. err = updateRequestAccessPointEndpoint(req, tv)
  127. if err != nil {
  128. req.Error = err
  129. }
  130. case arn.S3ObjectLambdaAccessPointARN:
  131. err = updateRequestS3ObjectLambdaAccessPointEndpoint(req, tv)
  132. if err != nil {
  133. req.Error = err
  134. }
  135. case arn.OutpostAccessPointARN:
  136. // outposts does not support FIPS regions
  137. if req.Config.UseFIPSEndpoint == endpoints.FIPSEndpointStateEnabled {
  138. req.Error = s3shared.NewFIPSConfigurationError(resource, req.ClientInfo.PartitionID,
  139. aws.StringValue(req.Config.Region), nil)
  140. return
  141. }
  142. err = updateRequestOutpostAccessPointEndpoint(req, tv)
  143. if err != nil {
  144. req.Error = err
  145. }
  146. default:
  147. req.Error = s3shared.NewInvalidARNError(resource, nil)
  148. }
  149. }
  150. func updateBucketEndpointFromParams(r *request.Request) {
  151. bucket, ok := bucketNameFromReqParams(r.Params)
  152. if !ok {
  153. // Ignore operation requests if the bucket name was not provided
  154. // if this is an input validation error the validation handler
  155. // will report it.
  156. return
  157. }
  158. updateEndpointForS3Config(r, bucket)
  159. }
  160. func updateRequestAccessPointEndpoint(req *request.Request, accessPoint arn.AccessPointARN) error {
  161. // Accelerate not supported
  162. if aws.BoolValue(req.Config.S3UseAccelerate) {
  163. return s3shared.NewClientConfiguredForAccelerateError(accessPoint,
  164. req.ClientInfo.PartitionID, aws.StringValue(req.Config.Region), nil)
  165. }
  166. // Ignore the disable host prefix for access points
  167. req.Config.DisableEndpointHostPrefix = aws.Bool(false)
  168. if err := accessPointEndpointBuilder(accessPoint).build(req); err != nil {
  169. return err
  170. }
  171. removeBucketFromPath(req.HTTPRequest.URL)
  172. return nil
  173. }
  174. func updateRequestS3ObjectLambdaAccessPointEndpoint(req *request.Request, accessPoint arn.S3ObjectLambdaAccessPointARN) error {
  175. // DualStack not supported
  176. if isUseDualStackEndpoint(req) {
  177. return s3shared.NewClientConfiguredForDualStackError(accessPoint,
  178. req.ClientInfo.PartitionID, aws.StringValue(req.Config.Region), nil)
  179. }
  180. // Accelerate not supported
  181. if aws.BoolValue(req.Config.S3UseAccelerate) {
  182. return s3shared.NewClientConfiguredForAccelerateError(accessPoint,
  183. req.ClientInfo.PartitionID, aws.StringValue(req.Config.Region), nil)
  184. }
  185. // Ignore the disable host prefix for access points
  186. req.Config.DisableEndpointHostPrefix = aws.Bool(false)
  187. if err := s3ObjectLambdaAccessPointEndpointBuilder(accessPoint).build(req); err != nil {
  188. return err
  189. }
  190. removeBucketFromPath(req.HTTPRequest.URL)
  191. return nil
  192. }
  193. func updateRequestOutpostAccessPointEndpoint(req *request.Request, accessPoint arn.OutpostAccessPointARN) error {
  194. // Accelerate not supported
  195. if aws.BoolValue(req.Config.S3UseAccelerate) {
  196. return s3shared.NewClientConfiguredForAccelerateError(accessPoint,
  197. req.ClientInfo.PartitionID, aws.StringValue(req.Config.Region), nil)
  198. }
  199. // Dualstack not supported
  200. if isUseDualStackEndpoint(req) {
  201. return s3shared.NewClientConfiguredForDualStackError(accessPoint,
  202. req.ClientInfo.PartitionID, aws.StringValue(req.Config.Region), nil)
  203. }
  204. // Ignore the disable host prefix for access points
  205. req.Config.DisableEndpointHostPrefix = aws.Bool(false)
  206. if err := outpostAccessPointEndpointBuilder(accessPoint).build(req); err != nil {
  207. return err
  208. }
  209. removeBucketFromPath(req.HTTPRequest.URL)
  210. return nil
  211. }
  212. func removeBucketFromPath(u *url.URL) {
  213. u.Path = strings.Replace(u.Path, "/{Bucket}", "", -1)
  214. if u.Path == "" {
  215. u.Path = "/"
  216. }
  217. }
  218. func buildWriteGetObjectResponseEndpoint(req *request.Request) {
  219. // DualStack not supported
  220. if isUseDualStackEndpoint(req) {
  221. req.Error = awserr.New("ConfigurationError", "client configured for dualstack but not supported for operation", nil)
  222. return
  223. }
  224. // Accelerate not supported
  225. if aws.BoolValue(req.Config.S3UseAccelerate) {
  226. req.Error = awserr.New("ConfigurationError", "client configured for accelerate but not supported for operation", nil)
  227. return
  228. }
  229. signingName := s3ObjectsLambdaNamespace
  230. signingRegion := req.ClientInfo.SigningRegion
  231. if !hasCustomEndpoint(req) {
  232. endpoint, err := resolveRegionalEndpoint(req, aws.StringValue(req.Config.Region), req.ClientInfo.ResolvedRegion, EndpointsID)
  233. if err != nil {
  234. req.Error = awserr.New(request.ErrCodeSerialization, "failed to resolve endpoint", err)
  235. return
  236. }
  237. signingRegion = endpoint.SigningRegion
  238. if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
  239. req.Error = err
  240. return
  241. }
  242. updateS3HostPrefixForS3ObjectLambda(req)
  243. }
  244. redirectSigner(req, signingName, signingRegion)
  245. }
  246. func isUseDualStackEndpoint(req *request.Request) bool {
  247. if req.Config.UseDualStackEndpoint != endpoints.DualStackEndpointStateUnset {
  248. return req.Config.UseDualStackEndpoint == endpoints.DualStackEndpointStateEnabled
  249. }
  250. return aws.BoolValue(req.Config.UseDualStack)
  251. }