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.

