WEBVTT 00:00.000 --> 00:13.360 Can you hear me now? Great. Hey everybody, I'm Stefano Ventasugia, and I'm a software engineering 00:13.360 --> 00:18.960 in reddit. And today I'm going to introduce you to a conformist, a pleasure in policy 00:18.960 --> 00:26.100 engine that my team and I are actively developing. Let's start from the problem. As the 00:26.100 --> 00:30.700 audience is in this room, you already know, we are seeing a boom in tools to generate 00:30.700 --> 00:38.060 this bombs. And we have plenty of, to generate, sorry, a secure supply chain, artifacts in general. 00:38.060 --> 00:42.660 And we have plenty of this bombs, a salsa, a provenance, a test stations, and vulnerability 00:42.660 --> 00:49.700 reports in our pipelines. So basically what we spent in the last years is to, so we spent 00:49.700 --> 00:55.800 the last years solving the generation problem. But we have another problem now. We have all 00:55.800 --> 01:02.040 this data, but who is actually very fine it? For example, if an SBOM contains a vulnerability, 01:02.040 --> 01:06.680 if a vulnerability is present in an SBOM number one checks, are we actually releasing our 01:06.680 --> 01:12.880 software securely? Of course not. This is why, if we don't have a mechanism to verify all 01:12.880 --> 01:18.280 these documents, all we have is passive compliance, not active security. And we cannot 01:18.280 --> 01:24.400 rely on human oversight either to verify these documents, because it cannot keep pace with 01:24.400 --> 01:33.200 the modern CICD deployment velocity. This is why we build conform. Conforma is an open source 01:33.200 --> 01:41.480 tool designed to see exactly in the gap between artifact generation and production deployment. 01:41.480 --> 01:49.000 It works by evaluating the documents generated by the supply chain and to evaluate them against 01:49.000 --> 01:54.440 a policy that is defined as a code. And finally, it outputs a clear pass fail signal that 01:54.440 --> 02:00.200 can be used in the pipeline. It also generates a human readable output to make the violations 02:00.200 --> 02:07.320 that are produced during the evaluation clearly understandable and to facilitate the remediation. 02:07.320 --> 02:12.720 But not least, it also, it is designed as a lightweight CLI tool that can be easily integrated 02:12.720 --> 02:19.840 in your pipeline, where it can serve as a blocking gate. This is how it works at a high 02:19.840 --> 02:25.080 level. The CLI takes the input, all the documents that are generated in your pipeline. 02:25.080 --> 02:31.040 So for example, image manifest, image signatures, SBOMs, sub-Provenants, attestations, but 02:31.040 --> 02:38.760 also CV scans, test reports, and basically whatever you want to validate. And evaluates 02:38.800 --> 02:45.760 them against a policy. That is a set of rules that enforce a variety of compliance checks 02:45.760 --> 02:51.840 against these documents. This policy is highly flexible because it can be adapted and 02:51.840 --> 02:58.840 parameterized using a policy configuration file. So that the same policy logic can be used 02:58.840 --> 03:06.080 with different security profiles for different environments. And finally, the CLI produces 03:06.080 --> 03:12.600 a simple output, as I said, that can be used as a blocking gate in your pipeline. 03:12.600 --> 03:19.840 Having the policy logic and the policy configuration version, there is a code in a Git repo. 03:19.840 --> 03:25.120 In a Git repo, offers several advantages. First of all, you can apply the same quality standards 03:25.120 --> 03:31.760 that you use for your application code, also for your security logic. Also, your policy 03:31.800 --> 03:37.400 and hence your security posture is fully versioned and though it will. You can also write 03:37.400 --> 03:44.400 unit tests for the rules you implement so that you are sure that they behave exactly as intended. 03:44.400 --> 03:49.760 And finally, the policy can be jointly maintained using a collaborative governance model 03:49.760 --> 03:59.000 where several teams contribute to the policy definition. We, in a reddit, adopted 03:59.000 --> 04:04.640 a conform on the majority of our bills and we aim to adopt it on the totality of our 04:04.640 --> 04:10.000 bills. At the following link, you can find the full list of checks that we enforce in our 04:10.000 --> 04:16.240 bills. And in this slide, they put the most interesting ones. So, for example, we have 04:16.240 --> 04:22.520 basic magic checks to check that to ensure that the basic magic that we use in our containers 04:22.600 --> 04:30.920 are trusted and come from trusted registries. As bomb checks, that enforce a variety of checks 04:30.920 --> 04:36.760 against the packages listed in our bomb, such as if they are allowed if they have only 04:36.760 --> 04:43.040 allowed attributes and so on. Several checks against the salsa provenance attestations 04:43.040 --> 04:50.200 to enforce salsa security levels. But also checks that ensure that the software is free 04:50.280 --> 04:55.160 of the CVs and checks that ensure that the software is being tested and that all the tests 04:55.160 --> 05:09.160 are green. But that's enough talk. Let's move to the demo. Let's start with a simple example. 05:09.160 --> 05:14.520 So, suppose we want to validate a simple JSON file that contains a list of animals belonging 05:14.600 --> 05:21.240 to different species. We implement our rule. This is written in a regal language. That is 05:21.240 --> 05:28.280 the declarative query language that is designed specifically to write policies. You can see 05:28.280 --> 05:33.560 here that we define some metadata to describe the rule and the steps that are needed to fix 05:33.560 --> 05:39.880 the violation. Then we have a line 10. Let's see if the list works. Yes. At line 10, 05:39.960 --> 05:45.080 the nigh contains result is the default pattern to define a violation in conforma. 05:47.240 --> 05:53.640 And then the rule simply iterates the list of animals and throws a violation if any snakes 05:53.640 --> 05:58.680 are found in it. So, basically what we are enforcing with this rule is that we are forbidden 05:58.680 --> 06:05.560 snakes from being on the list. Then we have our policy configuration that in our case 06:05.640 --> 06:14.040 simply includes our rule we just define. And that's it. Two files is all we need to run conforma. 06:14.040 --> 06:22.520 Is that simple? Let me show you the common. Is Evalidate input is the main CLI common. Then we pass 06:22.520 --> 06:28.440 the input JSON that we want to validate. The policy yamol that is the policy configuration that 06:28.440 --> 06:33.480 will be used for the validation. And then a couple of arguments or optional arguments to 06:33.480 --> 06:40.520 improve output readability. I also add the line to show you how the common produces a simple 0, 06:40.520 --> 06:48.360 1 exit code. And this is the output. Since we had as naked our list you can see that the validation 06:48.360 --> 06:55.240 failed with one violation. And you can see in the violation text all the metadata that we define 06:55.240 --> 07:00.440 to describe it. So, we have the reason why it failed that is pointing out specifically which 07:00.840 --> 07:07.960 any more is throwing it. And the solution steps to remediate it. Not is also how the common 07:07.960 --> 07:15.400 existed with a non-zero exit code because the validation failed. Now let's suppose we have 07:16.520 --> 07:21.800 choose nakes in our list. And let's see how the conforma output changes in this case. 07:22.040 --> 07:30.040 Now we have two violations, one for each snake. And again the reason of which violations 07:30.040 --> 07:36.200 shows exactly which snake is throwing it. So, this further facilitates the remediation. 07:37.000 --> 07:41.080 The common is still the validation is still failing. So, let's fix it now. 07:42.440 --> 07:47.320 By transforming our nakes into rabbits. So, let's run conforma again. 07:47.480 --> 07:55.480 And you can see that now the validation is passing. And the nose nake rules, nose nakes rule 07:55.480 --> 08:01.000 is successful now. Since the validation is passing the exit code is no 0. 08:03.880 --> 08:08.440 Sometimes we don't want to straight up deny something but just warn the user about potentially 08:08.440 --> 08:15.160 bad situations. So, for example, in our case we are okay with cats and dogs being in the same list. 08:15.160 --> 08:19.720 But we want to warn the user to be aware about potential fights. 08:21.160 --> 08:26.280 So, let's define our nangle rule. This is very similar to what we did before. Again, we have the 08:26.280 --> 08:32.360 metadata that describe it and the solution steps. Then we have worn contains result that is the 08:32.360 --> 08:38.280 default pattern to define our nangle. And then the rules simply check if there are both cats and dogs 08:38.360 --> 08:44.120 in the list and if so it throws the warning. Let's run conforma again. 08:47.000 --> 08:51.640 And you can see that now the validation is still successful and the exit code is 0. 08:51.640 --> 08:57.560 Because warnings are non-blocking. You can see that the warning is showing up in the result list 08:57.560 --> 09:03.320 again with all the information that we need. And we still have the nose nakes rule that is successful. 09:03.640 --> 09:12.440 Another useful feature for conforma is the possibility to include or exclude specific rules from 09:12.440 --> 09:18.040 the evaluation. So, for example, we are okay with the cats and dogs being in the same list. We 09:18.040 --> 09:23.000 don't care about the warning because we know that our cats and dogs get along well with each other. 09:23.000 --> 09:29.400 So, we want to exclude that warning. We created this section in the policy configuration file 09:29.480 --> 09:37.160 that lets exclude in the rule and run conforma again. You can see how the warning disappeared 09:37.160 --> 09:42.600 from the results list because the rule is not being evaluated anymore. Of course, you can do this 09:42.600 --> 09:53.480 also with violation rules. And you can also include only specific rules in your evaluation. 09:54.360 --> 09:58.600 Now, let's move to something more interesting. Let's validate an s-bomb. 10:00.040 --> 10:05.480 I created a simple s-bomb in a speed x format. You can see here the first lines. 10:06.840 --> 10:12.600 And you can see here the list of packages. I put two packages in it. One ledget one that is 10:12.600 --> 10:20.200 my app and one that simulates a package affected by a critical vulnerability. So, let's write our rule. 10:21.080 --> 10:26.760 This is slightly more complex than the previous ones. But basically what it does is to 10:26.760 --> 10:33.960 iterate the packages in the s-bomb and check if any of them is included in a user defined list of 10:33.960 --> 10:41.560 this allowed package. That is this rule data package scheme. And if one is found the violation 10:41.560 --> 10:46.440 is thrown and the failure message clearly points out which is the package that is allowed package. 10:47.160 --> 10:52.680 For this rule, I admit I cheated a bit because I imported a library of helper functions 10:52.680 --> 10:57.160 that we inform created to help navigate the main types of attestations. 10:59.320 --> 11:04.600 And this is our policy configuration file. In this case, we are not only importing the 11:04.600 --> 11:10.040 library and the rules definition, but we have also this new section rule data. Where we define 11:10.040 --> 11:17.960 the user data basically. The data, the configuration that the user use wants to use to 11:17.960 --> 11:23.320 for his evaluation. And here we can disallow the package version that is affected by the critical 11:23.320 --> 11:31.400 vulnerability. So, let's run conform against the s-bomb with this configuration. 11:33.960 --> 11:38.760 And the validation is failing because conform is correctly detecting the 11:38.760 --> 11:44.440 this allowed package. You can see again the steps that are needed to remedied the violation. 11:48.040 --> 11:55.000 So, let's suppose now that now we fix our application by bumping the package to a new 11:55.000 --> 12:00.520 major version that is not affected by the vulnerability and that we update this bomb so that it 12:00.520 --> 12:05.560 reflects the change. And in fact, you can see here that this bomb is listing version 2. 12:06.440 --> 12:15.800 So, let's run conform again. And you can see that now now the validation is successful, 12:16.360 --> 12:19.080 meaning that the s-bomb is compliant to our policy now. 12:21.960 --> 12:27.400 The last demo is the most interesting one. I created a simple repo. By the way, don't worry 12:27.400 --> 12:31.080 about coping the link. I will share it with you at the end of the presentation. 12:32.040 --> 12:36.440 And I will show you how we can validate this bombs and salsa program and statisticians 12:36.440 --> 12:42.280 that are being generated by our GitHub actions workflow. So, let's clone the repo first. 12:45.160 --> 12:50.920 Here we go. You can see here that it consists of a simple containerized 12:50.920 --> 12:57.960 going application. And for the sake of simplicity, I put in this same repo the policy rules and 12:57.960 --> 13:03.880 the policy configuration. However, for better management and security, they should leave in their own repo. 13:05.000 --> 13:10.200 By the way, this is easy to do because in the policy configuration, you can reference other 13:10.200 --> 13:19.960 policies, also by also using OCI tags or GitHub links, making referencing sources across repo 13:19.960 --> 13:26.280 very easy. This, for example, is a policy configuration file that we use in one of our redat 13:26.440 --> 13:36.280 repos. So, let's move on. You can see here that the goal and application has a dependency on 13:36.280 --> 13:41.160 0 log 1, 3, 2. The time to reduce the to show you how conformity else we did. 13:43.160 --> 13:47.880 And then we have a very simple container file that simply copies the binary, the newly built 13:47.880 --> 13:55.480 binary inside the container and then runs it. So, let's define our rules. The first one is the 13:55.480 --> 14:01.480 same that we use in the previous example, that just checks that the package is in an S-bomb 14:01.480 --> 14:07.960 or this allowed or not. And then we have this new rule that checks that the builder ID that 14:07.960 --> 14:17.480 is specified in our self-saprovenance is among the list, is among the list of allow the builder IDs. 14:18.440 --> 14:26.440 Again, here we have this allowed builder ID scheme that is as section defined in our policy 14:26.440 --> 14:30.440 configuration file. Similarly to what we did with our, this allowed the packages. 14:33.480 --> 14:38.760 And if the allowed the builder ID is not found, the exception is thrown. 14:41.240 --> 14:46.440 So, this is our policy configuration. You can see again, we are importing the policy files 14:46.520 --> 14:51.800 and we have this rule data where we are disallowing a 0 log version 131. 14:52.840 --> 14:57.720 Remind that in our application, we are using 132. So, this is okay. 14:58.520 --> 15:04.920 And we are allowing only one builder ID that is specific for my repo, specific for the release work flow 15:04.920 --> 15:09.240 that is specifically running on the main branch. So, this is very strict. 15:09.560 --> 15:18.600 And then we define our workflow. It will have three jobs, build, validate, and promote. 15:19.320 --> 15:22.840 What I am showing you here is the core step of the validate job. 15:24.040 --> 15:28.520 It is using the publicly available image of a conforma. 15:29.320 --> 15:35.080 Here for the sake of consistency, IP in that specific digest, but you could use the latest digest for 15:35.080 --> 15:43.000 simplicity. And I am calling here the conforma CLI using the validate image command. 15:43.800 --> 15:48.440 This is different from the validate input because it can run additional checks against the 15:48.440 --> 15:56.680 image being validated, such as image signature check, salsa provenance attestations, signature check, 15:56.680 --> 16:03.480 and format check. Here I am passing the image references input instead of a file. 16:03.560 --> 16:07.960 And we have also additional arguments that are needed for the signature verification. 16:09.560 --> 16:15.400 So, let's see what's the output of this workflow. By the way, this is an actual output 16:15.400 --> 16:22.440 that is taken by acquiring the GitHub API. So, you can also find the outputs of this workflow on GitHub itself. 16:23.320 --> 16:28.520 And you can see here that the validation is failed because we are running the release workflow 16:29.240 --> 16:36.920 on the main branch. And we have the analog version in our application. 16:36.920 --> 16:43.160 So, you can see that the three jobs are successful. The promote job only executes if the validate 16:43.160 --> 16:51.160 job is successful and it promotes the newly built artifact to the latest tag simulating our 16:51.160 --> 16:57.560 successful release. By the way, I didn't include it in my demo, but this workflow actually pushes the 16:57.640 --> 17:04.680 artifact on my personal image registry on Quay, whose link I will show you, I will share 17:04.680 --> 17:11.000 with you at the end of the demo. You can also see here in the workflow output and the workflow 17:11.000 --> 17:21.080 log that it's listing the artifacts that are being generated. So, let's now suppose that we 17:21.160 --> 17:27.080 change our policy and we now forbid a zero log versions up to one three two. 17:28.840 --> 17:33.880 You can also notice here that we have two policy configuration files, 17:33.880 --> 17:39.720 policy pull request and policy release. This is because we are using different security profiles for the same 17:41.000 --> 17:47.720 policy rules. In particular, with regards with the builder ID, because it is different if the 17:47.800 --> 17:54.520 workflow is run against the, against a pull request or against a, actually, this is a different 17:54.520 --> 17:59.240 workflow running on a pull request or the release workflow running on the main branch. 18:03.000 --> 18:09.640 The updated policy triggers a failing workflow. You can see here that the validate job failed. 18:11.000 --> 18:17.000 Let's take a look closer look at the conform output. You can notice here that there is the 18:17.000 --> 18:21.880 violation is expected pointing out that a zero log one three two is this allowed. 18:23.080 --> 18:27.000 And you can also see here that we have the other rules that are being evaluated, the 18:27.000 --> 18:34.440 first successful. So, we have the artistic signature check and syntax check, the image signature 18:34.440 --> 18:43.080 check and then our rule about the builder ID. So, we want to fix the violation. So, we open 18:43.080 --> 18:49.320 a PR. Oh, by the way, basically what we did before is to prevent a release of a software that 18:49.320 --> 18:56.360 is not compliant to our policy. So, we want to fix the violation now to be able to release again. 18:56.360 --> 19:04.680 So, we open a PR where we bump a zero log two one three three. And this is the workflow 19:04.680 --> 19:13.080 output running on the pull request. It is successful. So, it is the change we did made our code 19:13.080 --> 19:20.280 compliant for our policy. And notice here how the pull request workflow only has two jobs 19:20.280 --> 19:27.320 because it doesn't need to release the software of course. So, we can safely merge the change. 19:27.320 --> 19:33.400 You can see here how the change is reflected on the main branch. And finally, the workflow running 19:33.400 --> 19:39.240 against this commit is successful again. Meaning that we can safely release our application 19:39.240 --> 19:46.440 being sure that it is compliant to our policy. And that's it. Thank you for your attention. 19:46.440 --> 19:53.960 And if you have any one to download the link, you can find it here. Any questions? 19:53.960 --> 20:01.960 Questions? Questions? They're up there, someone? Yeah. 20:07.960 --> 20:09.960 Excuse me? 20:24.840 --> 20:44.040 I have a question. If you have, for example, two, the CV is in your code. And you fix one. Are 20:44.040 --> 20:50.440 you able to release again and just improve the situation gradually? 20:50.760 --> 20:59.080 Yes, basically, what you do with your policy depends on you. So, for example, you can 21:00.440 --> 21:06.440 temporarily exclude one specific CV from your evaluation or a set of, for example, 21:07.080 --> 21:13.080 based on severity of CVs. So, you can action it gradually. 21:13.080 --> 21:19.080 Yes? Maybe. 21:21.080 --> 21:38.360 Oh, really? Okay. So, let me share it. Maybe you can see here. It's basically this part. 21:38.920 --> 21:47.800 Without the build sample. So, I had true repo. Only with first them 20, 20, 6 is the one that 21:47.800 --> 21:57.080 links. Yes? I was wondering if you can use other sources to define these policies like 21:57.080 --> 22:01.880 checking on a data with the clinical bots on the end of your analysis. 22:01.880 --> 22:11.560 Oh, like the question is if you can use other sources to evaluate your policy, 22:11.560 --> 22:17.160 other source of truth, right? But you mean, on GitHub, like bugs? 22:24.440 --> 22:30.040 Okay. So, for example, if you on GitHub, you have a list of dependencies and this dependencies 22:30.040 --> 22:37.240 have some bugs open on GitHub, like GitHub issues, right? You could, for example, write in a rule 22:37.240 --> 22:43.960 that queries the GitHub API and checking if there are any bugs. This is an idea of I don't know if 22:43.960 --> 22:51.000 there are any tools that can do it. You could use the output from a task running that specific tool 22:51.000 --> 22:58.520 and evaluate that in a conforma. For example, we do this with vulnerability scans. We use 22:58.520 --> 23:05.080 clear scan in our pipeline that generates the vulnerability report and we only evaluate that 23:05.080 --> 23:09.480 report. We don't actually check vulnerabilities by ourselves. 23:12.520 --> 23:13.960 Yes? 23:13.960 --> 23:15.960 Oh. 23:15.960 --> 23:21.240 I was just wondering, like the inputs to your policy that they asked all the January update 23:21.240 --> 23:27.480 out of stations, like what kind of how are you produced and how are you that see on some 23:27.480 --> 23:32.520 code and use the input, but I just had a wonderful thing to use the support of the inputs 23:33.560 --> 23:35.320 and the policies. 23:35.320 --> 23:41.480 The question is how we generate this box or how we import them in our evaluation? 23:41.480 --> 23:46.120 Yeah, like a little bit of both and like, what things do you generally have, like, support, 23:46.120 --> 23:50.120 like, things like that? What do you support those inputs to get for this? 23:50.120 --> 23:54.200 Okay. What do you support as input in your policy? 23:56.600 --> 24:04.120 Basically, we use, when you call the validate image command, basically what the conforma does 24:04.120 --> 24:08.760 is to use the cosine non-load command that downloads all the 24:08.760 --> 24:11.240 attestations related to that image. 24:11.800 --> 24:19.880 So, basically that, but you could also attach to the, for example, our build system, 24:19.880 --> 24:26.920 attaches in the salsa provenance attestation, the full report of the query run with all the 24:28.200 --> 24:33.800 tasks that have been run parameters and outputs loads, so we are able to check basically 24:33.800 --> 24:36.600 everything that happened in our build pipeline. 24:36.680 --> 24:40.200 No, I'm not going to talk about that. 24:40.200 --> 24:46.440 Well, the question is sort of, as a community, I consider some very common policies appearing, 24:46.440 --> 24:52.120 so it is the way of sort of having a general policy that we can then sort of speak upon, 24:52.120 --> 24:55.240 because you've obviously demonstrated one of the spots specifically. 24:55.240 --> 24:56.360 Yeah, of course. 24:56.360 --> 25:01.400 The depth, actually, to the validating of picking up on that previous one, 25:01.560 --> 25:05.880 the build is because of the critical rule, the values of failure, if it took about a day, 25:05.880 --> 25:08.920 I guess, well, that's what's found. 25:08.920 --> 25:18.760 Yeah, the question is, is there are some default policies, like sets of rules to checks, 25:18.760 --> 25:25.400 like sets of things, no, vulnerability, or maybe just image checks and so on. 25:26.360 --> 25:32.600 We have some, in the metadata, we can define collections, so we have collections of rules, 25:33.960 --> 25:40.840 that checks, for example, salsa attributes, or we have a minimal set of policies that check, 25:41.800 --> 25:44.600 signature and not much more. 25:45.640 --> 25:54.440 So, yes, we have this function, it's not so extended, let's say, we could have more granularity, 25:54.520 --> 26:00.840 but the feature is there. For example, yes, there is the minimal collection, 26:00.840 --> 26:05.720 a collection called the minimal, that, as the basic set of policies that you can enforce, 26:05.720 --> 26:07.240 so you can start from there. 26:09.080 --> 26:12.280 Okay, times up, so thank you again. Right, thanks, Tejman.