Closed rpattcorner closed 4 years ago
Hi @rpattcorner . When you deploy the stack you can pass in the HTTP headers as a stack parameter. Among the HTTP headers are the CSP headers, that you can alter (or remove). If you've already deployed the stack, you can redeploy it while specifying a new version for the HTTP headers parameter, the redeploy will then work as an update for your stack.
E.g. if you wanna just remove the CSP header for now:
sam deploy \
--template-file packaged.yaml \
--stack-name "${YOUR_STACK_NAME}" \
--capabilities CAPABILITY_IAM \
--parameter-overrides Version=$(date +%s) HttpHeaders='{"Strict-Transport-Security":"max-age=31536000; includeSubdomains; preload","Referrer-Policy":"same-origin","X-XSS-Protection":"1; mode=block","X-Frame-Options":"DENY","X-Content-Type-Options":"nosniff"}' \
--region us-east-1
Let me know if that helps.
More docs would be great, I agree with you totally! Is it something maybe you wanna help out with, as you are going through "taking the deployed solution to the next step" now?
Many thanks! Somehow the fact that the CSP was being injected from a visible parm escaped me, and removing the header and updating CFN does indeed remove the failure.
Of course now we need to learn enough about CSP to deploy the correct headers rather than none at all, which is just how things go!
I notice that the update took a very long time (probably a combination of the CFront deploying to all regions and edge invalidation) and, more to interest, when the stack finally showed UPDATE_COMPLETE a number of individual events continue to show as as UPDATE_IN_PROGRESS, for example (HttpHeadersHandlerCodeUpdate, CheckAuthHandlerCodeUpdate) etc. as well as the event on the stack as a whole (CloudFrontDistribution). That's probably a CFN issue, but thought I'd call it out.
You're quite right, I should add to the documentation. At this stage all I have useful to say is how to deploy your own app 'on top of' the full deploy, but even that is probably worth putting in the README, along with your advice on removing CSP headers. Ideally other useful doc might include:
CreateCloudFrontDistribution
parameter that suppresses the bucket and distribution creationI'd like to see the project become a piece of boilerplate for folks who just want to put some decent authentication in front of a SPA - and see it is close. I've tried a number of other published approaches, which all have not worked, probably because of bitrot.
Again, thanks!
Sounds good. Let's keep this issue open until you've finished your setup, and ran into all the hurdles on your way. Then we can discuss what to add to the docs.
OK ... I have an update on the README that documents the CSP mod which is trivial, but I can send a PR for just that bit any time if you want. Next comes doc on trying to deploy on an existing bucket. Then ... maybe someday ... manual work we've carried out creating a Cognito Identity Pool so our app can access an AWS Role. I have the manual steps, but the new serverless build framework is new to me, so may take some time.
I'm getting doubts about Cognito based on this post but for now it's what we're using ... because you did and your app provides so much value!
FUD is ubiquitous but it sounds like the writer had a fair number of concrete instances.
Sure send me that PR on the CSP mod doc please. The smaller the PR's the better.
Regarding deploying to an existing bucket: that's also something I want to make possible in the deployment itself (#20 )
On Cognito: yes it has rough edges - but if you know what you're doing (/have good examples to follow) then you should be good.
@ottokruse
Some progress on controlling the s3 bucket based on the reuse-auth-only.yaml
example but the headers issue rears its head again.
The essence of the question I'm seeing is:
How can we use the
reuse-auth-template
pattern and override a parameter in the underlying SAM application. Specifically the HttpHeaders in this case, but other cases probably exist
From what I can see the reuse-auth-only.yaml
template leaves the Lambdas strictly alone, but to accomodate css we need to inject the http headers (without CSP in my case), just as the full deploy does. I'll experiment some, using the full template as a guide, but guidance on how to do so would be very welcome.
What I'm specifically seeing is:
reuse-auth-only.yaml
is a little misleading in that it looks like it has a complete CloudFront example in it, but in fact does not, as the OAI is missing.My goal is to produce a version of reuse-auth-only.yaml
that will actually run a (SPA mode) deployment while specifying the CDN and S3 artifacts.
The problem seems to be this:
reuse-auth-only
is based on not messing with building from source, but rather incorporating default behaviors, butreuse-auth-only
pattern.It looks like that kind of code modification is present in the full up template, but that it requires a custom cloudformation resource to alter the HttpHeaders lambda's configuration.txt. Is this correct? If so, do you have any suggestions on how to enhance the reuse-only.yaml
to enable the custom resource functionality? If not, other ideas?
So, doing this:
Resources:
LambdaEdgeExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- edgelambda.amazonaws.com
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
HttpHeadersHandler:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/lambda-edge/http-headers/
Handler: bundle.handler
Runtime: nodejs12.x
Role: !GetAtt LambdaEdgeExecutionRole.Arn
Timeout: 5
... more resources...
yields this deploy fail:
'CodeUri' is not a valid S3 Uri of the form 's3://bucket/key' with optional versionId query parameter..
In the full up template the Handler's CodeUri points to src/..., which makes sense as you're building in SAM.
Do you suppose there is a way in the reuse-auth-only
template there is a way to reference the CodeUri of the default HttpHeadersHandler that is being built behind the scenes as part of the nested stack, so we can pass the proper headers in? Per earlier note I suspect this requires enabling the custom resource in the CFN, or forking, building a SAM and modifying source, which I'd prefer to avoid.
I was stuck awhile because modifying configuration.json had no effect on the deployed app, until I discovered that you have to refresh the trigger, which is nearly invisible in the GUI. But doing so unblocks the desired CSS, so the question comes down to an economical way to enable the reuse
template to modify the published configuration.json like its big brother does ...
@ottokruse OK, been quite some time since I've looked at nested stacks and custom resources and much has changed.
I think I understand the pattern shown in the main template, where a custom resource hits a special lambda that modifies others of the lambdas based on params, which is what I want. But I'm missing a detail that will probably be obvious.
I'm wanting (per above) to modify the HttpHandler for a custom configuration for CSP, but within my own stack that consumes the app.
So, I see the app exports the CodeUpdateHandler as well as the HttpHeadersHandler, and I want to do something like this:
LambdaEdgeProtection:
Type: AWS::Serverless::Application
Properties:
Location:
ApplicationId: arn:aws:serverlessrepo:us-east-1:520945424137:applications/cloudfront-authorization-at-edge
SemanticVersion: 1.1.0
Parameters:
CreateCloudFrontDistribution: "false"
# AlternateDomainNames: egt-labs.com
HttpHeadersHandlerCodeUpdate:
Type: Custom::LambdaCodeUpdate
Properties:
ServiceToken: !GetAtt LambdaEdgeProtection.Outputs.CodeUpdateHandler
LambdaFunction: !GetAtt LambdaEdgeProtection.Outputs.HttpHeadersHandler
Version: !Ref Version
Configuration: !Ref HttpHeaders
Which I'd hoped would use the ServiceToken addtress to invoke the CodeUpdateHandler lambda, telling it to update the HttpHeadersHandler.
But there's some kind of permissions problem. Which you'd think would not be an issue since I'm asking the import of the app to update itself. I get the access denied exception below (account number redacted).
Can you see where I've gone wrong?
Failed to create resource. AccessDeniedException: User: arn:aws:sts::999999999999:assumed-role/jmpr-cupdate-1a-LambdaEdg-LambdaCodeUpdateHandlerR-J6DG2NJSDKDZ/jmpr-cupdate-1a-LambdaEdge-LambdaCodeUpdateHandler-LPKE2OLZ02K4 is not authorized to perform: lambda:GetFunction on resource: arn:aws:lambda:us-east-1:999999999999:function:jmpr-cupdate-1a-LambdaEdgeProte-HttpHeadersHandler-10JN8QX4GCAUE:1 at Object.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:51:27) at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/rest_json.js:55:8) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:683:14) at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10) at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:1
My goal is to produce a version of reuse-auth-only.yaml that will actually run a (SPA mode) deployment while specifying the CDN and S3 artifacts.
Awesome.
About the HTTP headers, can you not do it like this?:
LambdaEdgeProtection:
Type: AWS::Serverless::Application
Properties:
Location:
ApplicationId: arn:aws:serverlessrepo:us-east-1:520945424137:applications/cloudfront-authorization-at-edge
SemanticVersion: 1.1.0
Parameters:
CreateCloudFrontDistribution: "false"
# AlternateDomainNames: egt-labs.com
HttpHeaders: yourstuffhere
Can you see where I've gone wrong?
If you go the custom resource route (which I hope you do not have to) you need to give the Lambda handler of your custom resource permission to read and update the Lambda you want it to update. Did you do that?
Ah, so close and yet so far. Never occurred to me to pass the HttpHeaders directly to the app. That seems to be accepted by CFN.
So we get a full CFN run, and inspection shows that the HttpHeaders lambda has the correct configuration.json. Yet on run I see:
503 ERROR
The request could not be satisfied.
The Lambda function associated with the CloudFront distribution is invalid or doesn't have the required permissions. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront)
Request ID: c_PxcPfB_juqg2TVppHbTWsbcsUW3jOQULtO6N0EJT7hJMoa42TVdw==
Which is like nothing I ever saw, and incredibly vague. Any hints?
If I have to go back to the (clumsier) custom resource and adjust permissions, it sounds like you're saying I need to give the HttpHeadersHandlerCodeUpdate:
resource above the permission to modify the actual HttpHeadersHandler
in the nested stack. Did you mean in a similar manner to the snippet below from the full template?
LambdaCodeUpdateHandler:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/cfn-custom-resources/lambda-code-update/
Handler: index.handler
Runtime: nodejs12.x
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- lambda:GetFunction
- lambda:UpdateFunctionCode
Resource:
- !GetAtt ParseAuthHandler.Arn
- !GetAtt CheckAuthHandler.Arn
- !GetAtt HttpHeadersHandler.Arn
- !GetAtt RefreshAuthHandler.Arn
- !GetAtt SignOutHandler.Arn
The 503 looks like this item on the LambdaEdgePr-UserPoolDomainHandler. Never been a problem before. If you've seen it, let me know!
12:36:27
2020-05-11T12:36:27.256Z e9bae167-9a46-45ec-9f89-136fad11abb7 ERROR Can't parse physicalResourceId: undefined
2020-05-11T12:36:27.256Z e9bae167-9a46-45ec-9f89-136fad11abb7 ERROR Can't parse physicalResourceId: undefined
all I can find in the (cfn) doc is this:
physicalResourceId
Optional. The unique identifier of the custom resource that invoked the function. By default, the module uses the name of the Amazon CloudWatch Logs log stream that's associated with the Lambda function.
Sorry mate you run into every corner case possible it seems.
Is it an option for you to start development in a brand new stack? I think the errors you get now might be caused by having deployed the same stack several times, using different versions.
We should really get the "simple" option working for you, to inject the HTTP headers as app params. That should work - and if it does not, that is a bug we should fix.
Yes, this is challenging. Help is welcome.
I am deploying in a new stack each time ... not solid on stack updates so I've been avoiding them. A new deploy seems to still bring the 503, so no idea how to proceed.
The interactions between the CloudFront and Cognito have always been solid until this last injection of a parameter into the the application. Can't imagine what's changing.
Probably near the end of your day, but thoughts welcome
Can you paste your CFN template here? Then I can reproduce
May or may not be relevant ... I see in logs:
@timestamp
1589211025611
errorMessage
SyntaxError: Unexpected end of JSON input
errorType
Runtime.UserCodeSyntaxError
stack.0
Runtime.UserCodeSyntaxError: SyntaxError: Unexpected end of JSON input
stack.1
at _loadUserApp (/var/runtime/UserFunction.js:98:13)
stack.2
at Object.module.exports.load (/var/runtime/UserFunction.js:140:17)
stack.3
at Object.<anonymous> (/var/runtime/index.js:43:30)
stack.4
at Module._compile (internal/modules/cjs/loader.js:1156:30)
stack.5
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1176:10)
stack.6
at Module.load (internal/modules/cjs/loader.js:1000:32)
stack.7
at Function.Module._load (internal/modules/cjs/loader.js:899:14)
stack.8
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
stack.9
at internal/main/run_main_module.js:18:47
Doublechecking my headers param to be sure I'm not screwing up the JSON
Yeah, that's it. Many thanks for the direction ... the problem was indeed a bad default in the json parameter that HttpHeaders silently applies to the request to checkAuth, messing it up mightily.
Hopefully that will get us doing, and get the team a nice new PR with documentation. The reuse-auth-only
doesn't have a noSPA example, which is just as well, since I don't have testing for it, but we should have a SPA example shortly.
OK, PR's in. Thanks for the help!
Thanks for your help!
Closing this one as we have merged #55
We're working our way thru adapting this promising approach to securing an Angular JS app. In replacing the sample REACT application we are finding our CSS blocked, and it looks as if it's a problem with content security policy headers. There's good information in adding headers here, but because of the complexity of the authentication solution it's not clear where in the chain of requests and responses to add code to create the headers.
Do you have a suggestion or recommendation?
And a request ... it would be great to have a little documentation on discussion on taking the deployed solution to the next step and using it for an actual app, including issues like this.
Thanks for a promising (if complex) solution!