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

Add support for OCSP #1160

Closed
wants to merge 0 commits into from
Closed

Add support for OCSP #1160

wants to merge 0 commits into from

Conversation

fnikolai
Copy link
Contributor

@fnikolai fnikolai commented Dec 2, 2022

This PR implements the OCSP support discussed in #1095 and #1152.

The reference implementation is based on RFC https://www.rfc-editor.org/rfc/rfc6960

Tests has been based on this doc.
https://akshayranganath.github.io/OCSP-Validation-With-Openssl/

Step 1: Get the server certificate

openssl s_client -connect www.akamai.com:443 < /dev/null 2>&1 |  sed -n '/-----BEGIN/,/-----END/p' > certificate.pem

Step 2: Get the intermediate certificate

openssl s_client -showcerts -connect www.akamai.com:443 < /dev/null 2>&1 |  sed -n '/-----BEGIN/,/-----END/p' > chain.pem

Step 3: Run Singularity in OCSP mode

singularity verify --certificate ./certificate.pem --certificate-intermediates ./chain.pem --ocsp-verify image.sif

Expected outputs:

  • If communication error: FATAL: OCSP server is unavailable: certificate 'CN=www.akamai.com,O=Akamai Technologies, Inc.,L=Cambridge,ST=Massachusetts,C=US': failed to contact OCSP (additional information is printed to identify which certificate has failed)

  • If OCSP OK: FATAL: Failed to verify container: integrity: signature object 5 not valid: dsse: verify envelope failed: Accepted signatures do not match threshold, Found: 0, Expected 1 (reasonable since the certificate is just for testing the OCSP and not for validating the signature).

  • If use invalid certificates: FATAL: Failed to verify container: x509: certificate signed by unknown authority

// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package cli
Copy link
Member

Choose a reason for hiding this comment

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

If possible, this implementation should not be in the cmd/internal/cli package but somewhere appropriate under internal/pkg.

We're trying to move implementation logic out of the cli package, so that it really just has flag / option handling etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. It shouldn't be a problem.

Copy link
Member

@tri-adam tri-adam left a comment

Choose a reason for hiding this comment

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

Hi Fotis, thanks for the PR! I am on the road today and tomorrow, but thought I'd add a few initial thoughts.

I will try to get #1156 and then #1139 tidied up and ready for review ASAP...

Comment on lines 223 to 281
// If the certificate is self-signed certificate, we can use the certificate itself as one of the roots.
// Otherwise, we need the certificate of the authorities who signed the certificate.
var certificateIsSelfSigned bool

if string(cert.AuthorityKeyId) == string(cert.SubjectKeyId) {
selfRoot := x509.NewCertPool()
selfRoot.AddCert(cert)

opts = append(opts, singularity.OptVerifyWithRoots(selfRoot))
certificateIsSelfSigned = true
}

intermediateCertificates := certPool{}
rootCertificates := certPool{}

if cmd.Flag(verifyCertificateIntermediatesFlag.Name).Changed {
if certificateIsSelfSigned {
sylog.Infof("Ignore intermediate certificates due to the certificate being self-signed.")
} else {
intermediates, err := loadCertificatePool(certificateIntermediatesPath)
if err != nil {
sylog.Fatalf("Failed to load intermediate certificates: %v", err)
}

intermediateCertificates = intermediates
opts = append(opts, singularity.OptVerifyWithIntermediates(intermediates.x509CertPool()))
}
}

if cmd.Flag(verifyCertificateRootsFlag.Name).Changed {
if certificateIsSelfSigned {
sylog.Infof("Ignore root certificates due to the certificate being self-signed.")
} else {
roots, err := loadCertificatePool(certificateRootsPath)
if err != nil {
sylog.Fatalf("Failed to load root certificates: %v", err)
}

rootCertificates = roots
opts = append(opts, singularity.OptVerifyWithRoots(roots.x509CertPool()))
}
}

if cmd.Flag(verifyOCSPFlag.Name).Changed {
if certificateIsSelfSigned {
sylog.Infof("Ignore OCSP due to the certificate being self-signed.")
} else {
ocspErr := OnlineRevocationCheck(cert, intermediateCertificates, rootCertificates)
if errors.Is(ocspErr, errOCSPQuery) {
// TODO: We need to decide whether this should be strict or permissive.
sylog.Fatalf("OCSP server is unavailable: %v", ocspErr)
} else if ocspErr != nil {
sylog.Fatalf("OCSP verification has failed: %v", ocspErr)
}

sylog.Infof("OCSP validation has passed")
}
}

Copy link
Member

Choose a reason for hiding this comment

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

The cmd/internal/cli package shouldn't contain any logic aside from loading the certificates/pools specified on the command line and passing them on to the application code (internal/app/singularity.) The idea being that non-CLI code could just as easily need to verify a signature, and the behaviour shouldn't differ whether it's the user or Singularity itself calling the code.

In the context of adding online verification, this package should simply be appending to opts a functional (singularity.OptVerifyWithOCSP?) option defined in internal/app/singularity. Does that make sense?


// If the certificate is self-signed certificate, we can use the certificate itself as one of the roots.
// Otherwise, we need the certificate of the authorities who signed the certificate.
var certificateIsSelfSigned bool
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be wise to tackle any self-signed certificate functionality separate from the online verification work. We probably need to discuss what should happen when a self-signed certificate is specified. My gut instinct is that Singularity should either:

  • Not accept a self-signed cert unless it is present in the system pool, or
  • Accept it only if the user has specified this as the desired behaviour.

As an example of how this is handled elsewhere, wget --no-check-certificate allows a self-signed cert to be used. That being said, its documentation actually recommends the first approach for self-signed certs:

For self-signed/internal certificates, you should download the
certificate and verify against that instead of forcing this insecure mode.

Comment on lines 66 to 68
// certPool is needed because *x509.x509CertPool does not support listing of Certificates.
// Without listing, it is impossible to perform Revocation Checking.
type certPool map[string]*x509.Certificate
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand why we can't do revocation listing without defining this type?

The logic in internal/app/singularity is calling Certificate.Verify (here), which will return one or more certificate chains based on the certificate and pool(s) it is passed.

When --ocsp-verify is specified, I think what we'll need to do is add on to that by walk the certificate chain(s) and do OCSP verification (call queryOCSP) on each certificate in the chain. Does that make sense?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants