Conversation
munnerz
left a comment
There was a problem hiding this comment.
First of all, thanks very much for this contribution. It looks like a great start at a Vault issuer, and is definitely something we'll want to merge provided we can resolve some of the finer details.
I've added a few comments, and I'm keen to run e2e tests on this. There's one particular comment (left above) that is blocking end-to-end testing this (a change to the test fixtures, I guess that you made so you can test locally).
It also looks like you've used a different version of dep to update dependencies. I hope our cert-manager-quick-verify job will pick that up 😅 . In the meantime I'll add a update-deps.sh script that ensures versions are correct.
If you've got any questions (either about the review, or more generally), feel free to reach out either here or on the Kubernetes slack (my username is @munnerz).
| // Server is the vault connection address | ||
| Server string `json:"server"` | ||
| // Vault URL path to the certificate role | ||
| Path string `json:"path"` |
There was a problem hiding this comment.
This implies a 1:1 binding between Issuer and a role within a PKI backend. I think this makes sense to be honest, and I like keeping it simple here, but wanted to highlight it.
There was a problem hiding this comment.
Perhaps before 1.0 we could add some sort of 'role binding' support. This would probably be a breaking change... we'll see what users want.
pkg/issuer/vault/issue.go
Outdated
|
|
||
| const ( | ||
| // certificateDuration of 90 days | ||
| certificateDuration = time.Hour * 24 * 90 |
There was a problem hiding this comment.
Should probably be configurable - perhaps as a field on the issuer?
There was a problem hiding this comment.
I agree. Initially, I was hoping for a shorter certificate duration but I hit the default renewBefore in pkg/controller/certificates/sync.go:
const renewBefore = time.Hour * 24 * 30Maybe renewBefore and certificateDuration should be configurable? For Vault and CA issuers It could make sense I think. For ACME it is fixed to 90 days so It could be enforced at 30 days in that specific issuer.
There was a problem hiding this comment.
For ACME it is fixed to 90 days so It could be enforced at 30 days in that specific issuer.
Careful, that's not true for ACME in general and might be over-fitting Let's Encrypt. The protocol allows flexibility in certificate lifetime.
There was a problem hiding this comment.
Oh ok sorry about that. Thanks for pointing it out!
| token, err := vaultTokenRef(v.secretsLister, v.issuerResourcesNamespace, tokenRef.Name, "token") | ||
| if err != nil { | ||
| return nil, fmt.Errorf("error reading Vault token from secret %s/%s: %s", v.issuerResourcesNamespace, tokenRef.Name, err.Error()) | ||
| } |
There was a problem hiding this comment.
We should probably check here to see why it failed (via the k8s.io/apimachinery/pkg/api/errors package).
This isn't something that's handled well in the CA or ACME implementation currently, and I think would take some fairly significant refactoring to add retrospectively (as the top level sync function needs a way to return nil despite an error occurring).
When initially writing cert-manager, I created a TransientError type that could be used to indicate a sync should be retried. This would save all the refactoring, at the expense of needing to check for transient errors at various points (and/or not 'masking' errors with fmt.Errorf)
pkg/issuer/vault/issue.go
Outdated
| "exclude_cn_from_sans": "true", | ||
| } | ||
|
|
||
| url := fmt.Sprintf("/v1%s", v.issuer.GetSpec().Vault.Path) |
There was a problem hiding this comment.
I think v.issuer.GetSpec().Vault.Path should not be required to start with a /, as vault itself doesn't use this afaik.
If it does start with a /, we could also automatically strip it, thus allowing both. But this fmt.Sprintf should be using /v1/%s imo.
There was a problem hiding this comment.
I will change it for a path.Join so it will work in both cases.
pkg/issuer/vault/issue.go
Outdated
|
|
||
| defer resp.Body.Close() | ||
|
|
||
| vaultResult := VaultResult{} |
There was a problem hiding this comment.
I guess the vault package doesn't provide any pre-made structs for us to use?
There was a problem hiding this comment.
After some digging there is in fact some structs that we could use. Even some helper methods too.
| for _, v := range vaultResult.Data.Chain { | ||
| certBytes.WriteString(v) | ||
| certBytes.WriteString("\n") | ||
| } |
There was a problem hiding this comment.
This is okay I think, but it's worth noting right now we don't expose any way to obtain the CA that signed a Certificate via cert-manager (this is something that would be great to change, but I've not been able to find a way to get a CA certificate from an ACME server (cc @cpu)).
iirc, the certificate chain here doesn't actually include that CA certificate anyway so this should be okay to include here.
There was a problem hiding this comment.
I've not been able to find a way to get a CA certificate from an ACME server (cc @cpu)).
@munnerz For the V1 API the new-cert HTTP response includes a Link header with an up relation that points to where you can find the issuer certificate for the issued leaf certificate.
For the V2 API things are much easier. After finalizing an order the URL of the certificate in the order object returns the full PEM encoded certificate chain. When you GET the cert URL you'll get both the leaf and the intermediate(s).
Hope that helps!
| ) | ||
|
|
||
| const ( | ||
| successVaultVerified = "VaultVerified" |
There was a problem hiding this comment.
Not for you to fix as part of this PR: we should probably move these Reason messages into a generic package
| glog.Info(messageVaultVerified) | ||
| v.recorder.Event(v.issuer, v1.EventTypeNormal, successVaultVerified, messageVaultVerified) | ||
| v.issuer.UpdateStatusCondition(v1alpha1.IssuerConditionReady, v1alpha1.ConditionTrue, successVaultVerified, messageVaultVerified) | ||
| return nil |
There was a problem hiding this comment.
We should perform additional checks here if possible - i.e. ensure we can connect to Vault and ensure the path is valid for issuing certs.
| } | ||
| }) | ||
|
|
||
| }) |
| tag: build | ||
| pullPolicy: Never | ||
| repository: us.gcr.io/projet-labo-1/cert-manager-ingress-shim | ||
| pullPolicy: Always |
There was a problem hiding this comment.
This needs switching back - if you do this first, I can /test all this 😄 (can't verify what is in that image, so I'm reluctant to test this on our infrastructure!)
There was a problem hiding this comment.
My bad, this file slipped in the commit.
|
Thanks for the review! I'm going to start working on your requested improvements in the coming days. |
|
/ok-to-test |
4a11dce to
b4c65b6
Compare
c0a4ae8 to
a72fdbd
Compare
|
/retest |
eada339 to
82a305c
Compare
|
@vdesjardins would you mind splitting this into an "Update vendor" PR and "Vault issuer support" to make it easier to review? & if it isn't too much of a pain, keeping your changes split into multiple commits until we're ready to merge would be great 😄 (obviously I'm not asking you to retroactively do this!) |
|
@vdesjardins PR needs rebase |
|
No problem! I will split them.
I was in fact thinking about doing that after my last push! :-)
…On Wed, Feb 21, 2018, 07:56 James Munnelly ***@***.***> wrote:
@vdesjardins <https://github.com/vdesjardins> would you mind splitting
this into an "Update vendor" PR and "Vault issuer support" to make it
easier to review?
& if it isn't too much of a pain, keeping your changes split into multiple
commits until we're ready to merge would be great 😄 (obviously I'm not
asking you to retroactively do this!)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#292 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAEGERcMx1-6m4-iWKYFk8Zz8EITuKc5ks5tXBJ7gaJpZM4R6DbA>
.
|
ace1e76 to
48a3bcc
Compare
|
Is there anyone actively working on the pending changes? This feature is the only reason I'm not using certmanager at the moment and would be glad to help out if it'll get it out the gate faster. |
|
@mwthink yes I'm working on it! I need to finish writing the documentation. |
|
It looks like this PR may touch more dependencies than required... this commit adds nearly 2M lines of code, and removes 1M: a48908c I'd expect a large addition, but not such a large reduction in LoC. Could you take another look? If you drop that commit out of your tree (with something like |
f094e55 to
2b91fe2
Compare
|
I fixed the dependencies. Look much better now 😀 I did some commit cleanup too. |
munnerz
left a comment
There was a problem hiding this comment.
Nothing too major now, thanks very much for squashing - far easier to review!
Looking forward to getting this merged in 😃
| // Certificate default Duration | ||
| Duration time.Duration `json:"duration,omitempty"` | ||
| // Certificate renew before expiration duration | ||
| RenewBefore time.Duration `json:"renewBefore,omitempty"` |
There was a problem hiding this comment.
👍 this looks good to me.
We may in future want to consider switching this to some form of 'CertificatePolicy' resource (ie certificate-class), which can be associated with a set of certificates somehow. This would help admins provide cert-specific default config easily.
For now though, this seems acceptable. We can always retain this in future as the issuer default (where a CertificatePolicy would override the issuers defaults)
pkg/issuer/acme/issue.go
Outdated
| const ( | ||
| errorIssueError = "IssueError" | ||
|
|
||
| // default certificateDuration of zero, we let ACME decide |
There was a problem hiding this comment.
This const doesn't seem to be used in issue.go, and only in acme.go. Can you move it into acme.go?
There was a problem hiding this comment.
also rename to defaultCertificateDuration
pkg/issuer/ca/ca.go
Outdated
|
|
||
| if err := issuer.ValidateDuration(issuerObj); err != nil { | ||
| return nil, fmt.Errorf("CA %s", err.Error()) | ||
| } |
There was a problem hiding this comment.
Instead of checking ValidateDuration in the New* functions, we should perform it in Setup() and if they are invalid, mark the Issuer NotReady. This will cause Certificate requests to fail as well as a side effect, and providers a cleaner error reporting interface (we can add a status condition in future to explain why it failed, e.g. a ConfigValid status condition)
| // WaitForCertificateEvent waits for an event on the named Certificate to contain | ||
| // an event reason matches the supplied one. | ||
| func WaitForCertificateEvent(client kubernetes.Interface, cert *v1alpha1.Certificate, reason string, timeout time.Duration) error { | ||
| return wait.PollImmediate(500*time.Millisecond, timeout, |
There was a problem hiding this comment.
Reduce this to check every 1s or 2s? Not sure what sort of values are used for timeout here.
| return err | ||
| } | ||
|
|
||
| err = i.Setup(ctx) |
There was a problem hiding this comment.
Making the previously mentioned change to validate config in Setup(), we can revert the changes here and in issuers/sync.go
pkg/issuer/vault/setup.go
Outdated
| } | ||
|
|
||
| glog.Info(messageVaultVerified) | ||
| v.recorder.Event(v.issuer, v1.EventTypeNormal, successVaultVerified, messageVaultVerified) |
There was a problem hiding this comment.
Don't fire an event here either
pkg/issuer/vault/vault.go
Outdated
|
|
||
| if err := issuer.ValidateDuration(issuerObj); err != nil { | ||
| return nil, updateEventAndCondition(fmt.Errorf(messageVaultDurationInvalid, err.Error()), errorVaultDurationInvalid, issuerObj, recorder) | ||
| } |
There was a problem hiding this comment.
All validation of the Issuer resource should happen in Setup() - other fields (eg resourceNamespace) can be validated here, but not the issuer itself.
pkg/issuer/vault/vault.go
Outdated
| recorder.Event(issuerObj, v1.EventTypeWarning, event, err.Error()) | ||
| issuerObj.UpdateStatusCondition(v1alpha1.IssuerConditionReady, v1alpha1.ConditionFalse, event, err.Error()) | ||
| return err | ||
| } |
There was a problem hiding this comment.
Probably remove this function after changes above
| }, nil | ||
| } | ||
|
|
||
| func (v *VaultInitializer) Setup() error { |
There was a problem hiding this comment.
This looks stressful to maintain 😅
It may be best to move all the vault specific test utils into their own package so you can add an OWNERS file in there to be pinged for code reviews in future.
There was a problem hiding this comment.
Tests for our tests anyone? 😁
This can be deferred for now though.
| the Vault server base URL. The secret created previously is referenced in the issuer | ||
| with its *name* and *key* corresponding to the name of the Kubernetes secret and the | ||
| property name containing the token value respectively. | ||
|
|
There was a problem hiding this comment.
A small addition to explain that these Issuers can be referenced like any other and/or an example of a certificate now referencing the issuer we just created would be great.
I'm envisioning the tutorials section to be quite hand-holdy and explain how to get from point A to point B (i.e. no certs to signed cert).
There was a problem hiding this comment.
May also be worth creating an OWNERS file in this directory too
|
Let's get this in and see how we get on - we can iterate from there 😄 Thanks so much for all your hard work @vdesjardins! 🙏 👏 /lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: munnerz The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
What this PR does / why we need it:
Initial Vault support.
Which issue this PR fixes
fixes #17
Special notes for your reviewer:
Where I work we need support for Vault so I took a shot at it. Please tell me if you guys are interested in what I did. If yes I can write some documentation and rework the code if need be.
Thanks