-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: ele-4721 add support for security headers for cdn site hosting #60
Conversation
Fix @types/prettier to <2.6.0 to resolve a bunch of build errors that started happening - see comment and discussion here - https://github.com/DefinitelyTyped/DefinitelyTyped/ discussions/60310#discussioncomment-2811465
…aders_in_cdn_site
BREAKING CHANGE: This will change the CDK class from `cloudfront.CloudFrontWebDistribution` to `cloudfront.Distribution` for the cdn-site-hosting-construct. This will create a slightly different cfn template and so will deploy a new stack instead invalidating the old one. This will then break as the dns as it will be the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any way to test that these new default security headers are applied to a stack, and that they can be overridden, in our tests? https://github.com/talis/talis-cdk-constructs/blob/fb16b6735fbaf8280ab967d3abf55048ab6c27d5/test/infra/cdn-site-hosting/cdn-site-hosting-construct.test.ts
), | ||
{ | ||
aliases: [siteDomain], | ||
sslMethod: cloudfront.SSLMethod.SNI, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Has this been renamed to sslSupportMethod
below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
{ | ||
aliases: [siteDomain], | ||
sslMethod: cloudfront.SSLMethod.SNI, | ||
securityPolicy: cloudfront.SecurityPolicyProtocol.TLS_V1_1_2016, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Has this been renamed to minimumProtocolVersion
below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
props.certificateArn | ||
), | ||
{ | ||
aliases: [siteDomain], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Has this been renamed to domainNames
below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
// We use a custom origin rather than S3 origin because the latter | ||
// does not seem to support websiteErrorDocument correctly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Is this no longer an issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no custom origin support in the new class as far as I am aware, I tried going to a bogus page in Otis in a stack I bought up and it returned the styled Otis error page rather than something from s3 so assume it works in the new class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the customOriginSource
was a hold-your-nose solution because the default documented solution for attaching to an S3 bucket didn't seem to work via CDK.
This may be another benefit of the change, rather than something we need to recreate.
errorCode: 404, | ||
responseCode: 200, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Have these been renamed httpStatus
and responseHttpStatus
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
contentTypeOptions: { override: true }, | ||
// https://web.dev/security-headers/#recommended-usages-4 | ||
frameOptions: { | ||
frameOption: cloudfront.HeadersFrameOption.DENY, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment: I guess we'll have to override this in Otis, for LTI, etc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, but for most apps it should be deny which is why I have set it as DENY (recommended).
const defaultSecurityHeaders: cloudfront.ResponseSecurityHeadersBehavior = { | ||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src | ||
contentSecurityPolicy: { | ||
contentSecurityPolicy: "default-src 'self';", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: how have we decided on the defaults for all of these headers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just taken all the defaults from the recommended from commented link above it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only setting default-src
. Is this default strong enough on all the other CSP directives?
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... though it's good it's covering the major fetch directives...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will this affect authentication in our SPAs?
Yeah I would like to add some tests - I tried it in here:
But the stack just has a name for the security headers policy not the details of it itself so I wasn't sure how to test it? Open to adding tests if someone knows how. |
I would have a poke around using It seems to have more detail about resources in the stack and their properties |
Template is waaay better thanks! Added some tests. |
Our preference, IIUC, is that this is much simpler, and avoids running hand-crafted Lambdas on every request.
Sounds like a strong benefit.
These seem good to me. I wonder, though: when we cannot provide the headers in this way using the defaults, how are we going to add the security headers in? Do we already have a plan for that?
Until that's made official by deprecation policies, we should not assume this to be the case - it could easily be heresay. I'm more interested in what we will lose than what we will gain. Do we know what this might be?
Irrespective of the need for a new new stack, this would be a breaking change predominantly because it changes the external interface of the constructs. How is the DNS controlled? Asking because I thought it was part of the construct... Does this mean that applications using this will not be able to "simply run |
@@ -22,7 +23,7 @@ export interface CdnSiteHostingConstructProps | |||
*/ | |||
export class CdnSiteHostingConstruct extends cdk.Construct { | |||
public readonly s3Bucket: s3.Bucket; | |||
public readonly cloudfrontWebDistribution: cloudfront.CloudFrontWebDistribution; | |||
public readonly cloudfrontDistribution: cloudfront.Distribution; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we checked the projects to see if we're relying on this in some way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't but as I was intending a breaking change to the project I thought it wouldn't matter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might just help highlight anything where we're relying on this for something that will no longer be available. In all likelihood, we won't be relying upon it in this way, but if we were doing that and several of the apps were then blocked from upgrading, the construct's usefulness would substantially lower.
Might be worth emailing round the devs to ask anyone who is using this to take a look at their own project and feed back (by a short deadline). I'll look at user-utils, for instance.
new targets.CloudFrontTarget( | ||
this.cdnSiteHosting.cloudfrontWebDistribution | ||
) | ||
new targets.CloudFrontTarget(this.cdnSiteHosting.cloudfrontDistribution) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC, this will be used for non-production stacks, so we could use cdk destroy
then cdk deploy
without concern.
region: "eu-west-1", | ||
// region for Cloudfront Distribution certificates must be us-east-1 | ||
// https://stackoverflow.com/questions/37289994/aws-certificate-manager-do-regions-matter | ||
region: "us-east-1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is intended to represent the environment in which we are provisioning the entire stack, not just a Cloudfront Distribution certificate.
Let's say we wanted to add more resources to the construct that are region-specific, so we have a mixture of things that are supposed to be statically us-east-1
and things that are supposed to be provisioned in the specified region. If we use us-east-1
as our test case, we would not be able to successfully test that our our region specifications are correct, since it'd be entirely possible that either:
- We'd hard-coded everything to a single region, erroneously.
- We'd made everything respect the specified region when some things should be hard-coded to
us-east-1
.
For this reason, I'd be reluctant to hard-code to us-east-1
.
(To mitigate point 1, we should ultimately be testing to ensure that it respects different specified regions.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your broader point about us-east-1
would then involve hard-coding that correct region into line 20, IIUC.
Thanks for the great feedback!
Not sure I follow here, if people do not want to use the defaults in this project they can pass in a For example in Otis I plan to do: const securityHeaders: cloudfront.ResponseSecurityHeadersBehavior = {
contentSecurityPolicy: {
override: true,
contentSecurityPolicy:
"default-src 'none'; script-src 'self' *.talis.com *.pendo.io *.instana.io" +
" static.zdassets.com *.wistia.com zendesk-eu.my.sentry.io 'unsafe-inline';" +
" connect-src 'self' *.talis.com ekr.zendesk.com talisaspire.zendesk.com " +
"*.wistia.com *.instana.io; img-src 'self' data: *.pendo.io *.talis.com; " +
"font-src 'self' data: fonts.gstatic.com; style-src 'self' 'unsafe-inline'" +
" fonts.googleapis.com; base-uri 'self'; form-action 'self'; manifest-src 'self'",
},
// Otis is rendered in an iframe in the VLE
frameOptions: undefined,
};
That's a good point, I'm not entirely sure as I have not been able to find a direct comparison and the properties on the respective classes are often named differently so it can be hard to determine. Looking at Github issues there are certainly several requests to port features over from
Oh the DNS isn't controlled in any different way to how it was before I believe, what I meant was that people found the deployment failed when trying to switch over as CDK would try to create a new stack with the same domain and therefore DNS.
Applications can run cdk deploy but they need to ensure that the stack has a different name first and then afterwards delete their old stack. Alternatively, they could delete the existing stack and then deploy as usual but then there would be more downtime. |
That's a really useful nugget we should be sharing in the changelog. |
The question is poorly asked. I mean: if we cannot use security headers, and need to provide the security programmatically, do we already have a plan for that? The only reason I was asking is to see if the presence of these defaults is likely to pose problems, but honestly I cannot see how - just disable them, as you say. Ignore this. |
I think this is an important thing to understand. Obviously we are marking this as a breaking change, but it means that, all apps will have to do this on the next release after upgrading to this version of the library - even if the reason for the upgrade is related to a different construct. Doing this will not necessarily be trivial. For example, doing this in If these upgrades are left until the next time we want to make a change in those applications, it will be a nasty trip wire for someone to run up against when trying to make some other change. @ShaunParsons Should we have a plan to make this upgrade, reprovision stacks, migrate data etc, for all the consuming applications, so that this doesn't become an unexpected chore when trying to make a small change/fix in the future. |
Brilliantly observed, @malcyL. This is why I think we really need to go easy on "make everything a construct" until we've really understood complexities. It is clearly not always straightforward to update the infrastructure just by having a construct. It's also clear that breaking changes in one construct will impact on the entire library.
We might, but we haven't merged it yet. Maybe this is a sign that we should instead take a step back and consider our options. First, perhaps we need a replacement construct rather than an update, so we can deprecate this one, and apps are back in control of the migration. They could even bring up both constructs, swap the DNS, then remove the old construct. Second, perhaps we go back to What other options might be available to us? Are any of those any better at backwards compatibility? |
+1 to this - unless there is another option that doesn't require this at all I also think this should make us look at smalti because of what this has highlighted:
Update:
|
So to clarify, shall I make a new construct to avoid the breaking changes? |
I don't want to confuse things, but I am wondering if this is something worth mentioning - something I am looking at at the moment. This is a discussion on refactoring with the CDK constructs, problems this creates with logical id's, and a possible way around this using I know that you have changed the class your using in the CDK from Don't know how it's going to turn out for my problem yet, but perhaps it could help. |
The problem with overriding the logical id is that you need to know the logical id of the old stack first - which means that you need to pass this into |
@Crevitus I don't think we should be rushing to implement that. If you want to implement urgently, a separate construct gives you a pathway that doesn't break everything else. If it's not urgent that we implement, we should be spiking the above to understand it before we commit to it. I'd at least sleep on it for the weekend and see how you feel on Monday - or widen the discussion if you want to consider more options. But that's obviously more time... |
If this is a breaking change, then it should be a major version bump. We then need a way to communicate with the teams that we need to create tickets to upgrade to the latest version and deal with those breaking changes. This is the first time we're experiencing this pain with the shared constructs.
So, Essentially, if our Cloudformation stacks don't have any persistence, replacing them is trivial. In K8s, the databases are completely independent of the pods, this allows us to replace pods just because we can. We need to get to the same state with Cloudformation stacks. I guess what I'm proposing is:
I don't think we're going to get much further discussing this on this PR, so I propose @Crevitus finds a time in the calendar for us all to have a chat about this and our next steps forwards. I also think we need to update Infrastructure Best Practices to say that persistent storage should be deployed independently of our business logic. |
+1 I would definitely like to be involved in this meeting please @Crevitus |
@malcyL @junglebarry @ShaunParsons @pogotc - closing this after discussion documented here: https://docs.google.com/document/d/13taKSjJ222lrjSrby4iREL7RioCWOjCYk8ZO7bh-ClA/edit?usp=sharing The current approach will be to create a new construct with the new functionality to avoid breaking changes. This change will be made in a new PR. |
Problem
In Otis, we wish to add security headers to requests - talis/elevate-app#4721
After some investigation and chatting with platform and David, the consensus is that we would prefer to do this in the CDK constructs rather than adding a lambda to intercept responses.
We can do this using the new config that Amazon now has for the
cloudfront.Distribution
class - https://aws.amazon.com/blogs/networking-and-content-delivery/amazon-cloudfront-introduces-response-headers-policies/Solution
Currently for static sites
talis-cdk-constructs
is using thecloudfront.CloudFrontWebDistribution
class that does not support adding security headers. In order to add security headers we have converted to the newDistribution
class.Some sensible defaults have been chosen so that all projects using
cdn-site-hosting-construct
in the future will get security headers by default. All the headers can be overridden individually by the client projects.It seems that
CloudFrontWebDistribution
is no longer getting new features and may soon be deprecated so switching over to the newDistribution
class has multiple benefits: aws/aws-cdk#17755 (comment)As this creates a different config underneath (aws/aws-cdk#12707) client projects would have to create a new stack (using some date or number prefix) and then when ready switch the DNS over to the new stack.
I will, therefore, mark this as a breaking change to the project.