Skip to content
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

Introduce certificate pool structure and remove multiple encode/decode process #375

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

arsenalzp
Copy link
Contributor

@arsenalzp arsenalzp commented Jun 30, 2024

This PR introduce certificate pool structure which is mainly used to hold Certificates.
I want to decouple PEM operations from Certificates pool ones.
Deduplication option for certificate pool was also introduced - deduplication process can be optionally disabled.
It is related to #305

@cert-manager-prow cert-manager-prow bot added the dco-signoff: yes Indicates that all commits in the pull request have the valid DCO sign-off message. label Jun 30, 2024
@cert-manager-prow
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign sgtcodfish for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@cert-manager-prow
Copy link
Contributor

Hi @arsenalzp. Thanks for your PR.

I'm waiting for a cert-manager member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@cert-manager-prow cert-manager-prow bot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jun 30, 2024
@erikgb
Copy link
Contributor

erikgb commented Jul 1, 2024

/ok-to-test

@cert-manager-prow cert-manager-prow bot added ok-to-test and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jul 1, 2024
@arsenalzp
Copy link
Contributor Author

Let me fix typos!

Copy link
Contributor

@erikgb erikgb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this @arsenalzp! I did a first review pass. Please take a look!

pkg/bundle/source.go Outdated Show resolved Hide resolved
pkg/bundle/source.go Outdated Show resolved Hide resolved
pkg/bundle/bundle.go Outdated Show resolved Hide resolved
pkg/util/cert_pool.go Outdated Show resolved Hide resolved
@@ -121,3 +104,29 @@ func DecodeX509CertificateChainBytes(certBytes []byte) ([]*x509.Certificate, err

return certs, nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the following functions should have CertPool as receiver. Why not?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we could rename the functions as follows:

  • GetSplitPEMBundle -> AsSplitPEMBundle
  • GetSplitPEMBundleBytes -> AsPEMBundleBytes
  • GetSplitPEMBundleStrings-> AsPEMBundleStrings
  • GetCertsList -> AsCertificateList

Copy link
Contributor Author

@arsenalzp arsenalzp Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions were renamed.
Regarding CertPool as argument for DecodeX509CertificateChainBytes().
Let me explain: an idea here is PEM is responsible for providing prepared data for consumers in form that is needed by consumers, consumers by themself shouldn't know how, where data was taken from, CertPool is responsible for store, manage, filter, de-duplicate, etc of Certificates, it provides data for PEM, so end consumers shouldn't know how CertPool works inside (encapsulation).
Imagine, we want to provide DER encoded data instead of PEM one.
Moreover, I'm thinking about making CertPool abstract structure (interface).

Copy link
Member

@maelvls maelvls Jul 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding CertPool as argument for DecodeX509CertificateChainBytes()

I think Erik was referring to the functions below (AsSplitPEMBundle, AsPEMBundleBytes, AsCertificateList, GetCertificatesQuantity) rather than DecodeX509CertificateChainBytes.

An idea here is PEM is responsible for providing prepared data for consumers in form that is needed by consumers, consumers by themself shouldn't know how, where data was taken from, CertPool is responsible for store, manage, filter, de-duplicate, etc of Certificates, it provides data for PEM, so end consumers shouldn't know how CertPool works inside (encapsulation).

I'm not sure what PEM refers to. Is it a type that will be created in the future and that, for now, exists through the existence of the AsPEM functions?

Imagine, we want to provide DER encoded data instead of PEM one.

I imagine that CertPool would just need to have a couple of receiver functions:

func (*CertPool) AsDER() [][]byte
func (*CertPool) AsPEMBundle() []byte
func (*CertPool) AsPEM() [][]byte
func (*CertPool) Count() int

Why is adding receiver functions a bad idea? I don't see the benefits of encapsulating the "export" (DER or PEM) logic.

Moreover, I'm thinking about making CertPool abstract structure (interface).

What would be the benefits of turning this into an interface? Would that be for testing purposes?

@inteon inteon changed the title Introduce certitiface pool structure and remove multiple encode/decode process Introduce certificate pool structure and remove multiple encode/decode process Jul 3, 2024
@arsenalzp arsenalzp force-pushed the multipleDecode branch 4 times, most recently from aac3ec6 to a200310 Compare July 3, 2024 21:10
pkg/bundle/source.go Outdated Show resolved Hide resolved
pkg/util/pem.go Outdated Show resolved Hide resolved
pkg/util/cert_pool.go Outdated Show resolved Hide resolved
Copy link
Contributor

@erikgb erikgb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are approaching the finish line here, but there are still some "style" comments I would like to see fixed. :-)

pkg/bundle/source.go Outdated Show resolved Hide resolved
pkg/util/cert_pool.go Outdated Show resolved Hide resolved
pkg/util/cert_pool.go Outdated Show resolved Hide resolved
pkg/util/cert_pool_test.go Outdated Show resolved Hide resolved

func GetCertificatesQuantity(certPool *CertPool) int {
return certPool.size()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we just export the size function and delete this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be easy to calculate a size for other kind of storages in the future. As was stated earlier it was done because I wanted to encapsulate the insides of Certificates pool.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I don't understand your argument, but this can stay - it's "just" a name. 😉 If you still want to alias the Size function I think GetCertificateCount could be a better name.

Copy link
Member

@maelvls maelvls Jul 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to encapsulate the insides of Certificates pool.

Thanks for the clarification.

Can you elaborate on the need for encapsulating CertPool's behavior, and why the count of certificate in the certificate pool shouldn't be exposed?

I don't think that the behaviors "counting certificates", "exporting certificates to a PEM bundle", and "exporting certificates to a slide of DER bytes" need to be encapsulated away. More generally, I'm not convinced that encapsulation will solve anything in this context. I'd love to know more, though!

Copy link
Contributor Author

@arsenalzp arsenalzp Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello colleagues,
Behavior of CertPool should be hidden from its consumers. For example we are going to substitute current CertPool by one from x509 package or even third-party. As little possibilities to works with CertPool directly are given to consumers, as little components coupling we have.
From my opinion CertPool acts like io.Writer or io.Reader.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior of CertPool should be hidden from its consumers. For example we are going to substitute current CertPool by one from x509 package or even third-party. As little possibilities to works with CertPool directly are given to consumers, as little components coupling we have. From my opinion CertPool acts like io.Writer or io.Reader.

This does not make sense to me. Creating util-wrapper functions does not change your stated concern. It just looks very strange to me to have dummy single-statement functions. Making the CertPool type internal probably makes sense - to allow ourselves more freedom to modify it over time.

test/env/data.go Outdated Show resolved Hide resolved
@cert-manager-prow cert-manager-prow bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 8, 2024
@erikgb
Copy link
Contributor

erikgb commented Jul 8, 2024

@arsenalzp I have a big refactoring incoming: #378. It would be nice to get this PR merged soon. 😉

@arsenalzp
Copy link
Contributor Author

@arsenalzp I have a big refactoring incoming: #378. It would be nice to get this PR merged soon. 😉

Hello,
Can it wait until this evening?

@erikgb
Copy link
Contributor

erikgb commented Jul 8, 2024

Hello, Can it wait until this evening?

Sure, there is no urgency. I just have some extra time for open source work this week.

@arsenalzp arsenalzp force-pushed the multipleDecode branch 2 times, most recently from e0e7df7 to 4cb0887 Compare July 8, 2024 21:59
@cert-manager-prow cert-manager-prow bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 8, 2024
@arsenalzp
Copy link
Contributor Author

Hello, Can it wait until this evening?

Sure, there is no urgency. I just have some extra time for open source work this week.

Done

pkg/bundle/source_test.go Show resolved Hide resolved
pkg/bundle/source.go Outdated Show resolved Hide resolved
@@ -99,27 +98,19 @@ func (b *bundle) buildSourceBundle(ctx context.Context, sources []trustapi.Bundl
return bundleData{}, fmt.Errorf("failed to retrieve bundle from source: %w", err)
}

opts := util.ValidateAndSanitizeOptions{FilterExpired: b.Options.FilterExpiredCerts}
sanitizedBundle, err := util.ValidateAndSanitizePEMBundleWithOptions([]byte(sourceData), opts)
err = util.ValidateAndSplitPEMBundle(certPool, []byte(sourceData))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the function name a bit strange when used in this context. It is not evident, at least not to me, that the certs from sourceData are appended to certPool.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree with you, we can improve naming. Which suggestions ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err = util.ValidateAndSplitPEMBundle(certPool, []byte(sourceData))
err = certPool.AppendCertsFromPEM([]byte(sourceData))

Similar to https://pkg.go.dev/crypto/x509#CertPool.AppendCertsFromPEM.

In general, let us use functions with CertPool as receiver, and avoid the dummy "utils" wrapper functions. As noted many times already in reviews of this PR. 😸

pkg/fspkg/package.go Outdated Show resolved Hide resolved
pkg/util/cert_pool.go Outdated Show resolved Hide resolved
pkg/util/pem_test.go Show resolved Hide resolved
test/env/data.go Show resolved Hide resolved
test/env/data.go Outdated Show resolved Hide resolved
test/env/data.go Outdated Show resolved Hide resolved
test/env/data.go Outdated Show resolved Hide resolved
Signed-off-by: Oleksandr Krutko <alexander.krutko@gmail.com>

put some remarks from PR discussion

Signed-off-by: Oleksandr Krutko <alexander.krutko@gmail.com>

align with project style

Signed-off-by: Oleksandr Krutko <alexander.krutko@gmail.com>
@@ -99,27 +98,19 @@ func (b *bundle) buildSourceBundle(ctx context.Context, sources []trustapi.Bundl
return bundleData{}, fmt.Errorf("failed to retrieve bundle from source: %w", err)
}

opts := util.ValidateAndSanitizeOptions{FilterExpired: b.Options.FilterExpiredCerts}
sanitizedBundle, err := util.ValidateAndSanitizePEMBundleWithOptions([]byte(sourceData), opts)
err = util.ValidateAndSplitPEMBundle(certPool, []byte(sourceData))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err = util.ValidateAndSplitPEMBundle(certPool, []byte(sourceData))
err = certPool.AppendCertsFromPEM([]byte(sourceData))

Similar to https://pkg.go.dev/crypto/x509#CertPool.AppendCertsFromPEM.

In general, let us use functions with CertPool as receiver, and avoid the dummy "utils" wrapper functions. As noted many times already in reviews of this PR. 😸


func GetCertificatesQuantity(certPool *CertPool) int {
return certPool.size()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior of CertPool should be hidden from its consumers. For example we are going to substitute current CertPool by one from x509 package or even third-party. As little possibilities to works with CertPool directly are given to consumers, as little components coupling we have. From my opinion CertPool acts like io.Writer or io.Reader.

This does not make sense to me. Creating util-wrapper functions does not change your stated concern. It just looks very strange to me to have dummy single-statement functions. Making the CertPool type internal probably makes sense - to allow ourselves more freedom to modify it over time.

@arsenalzp
Copy link
Contributor Author

arsenalzp commented Jul 12, 2024

I didn't want to change code drastically.
OK, I will re-write it with receivers :)

@cert-manager-prow cert-manager-prow bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 15, 2024
@cert-manager-prow
Copy link
Contributor

PR needs rebase.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dco-signoff: yes Indicates that all commits in the pull request have the valid DCO sign-off message. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. ok-to-test size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants