acme: Add API fields for ExternalAccountBinding

Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
James Munnelly 2019-12-03 17:01:37 +00:00
parent 4073080089
commit 80bc253d74
10 changed files with 321 additions and 0 deletions

View File

@ -59,6 +59,44 @@ spec:
email:
description: Email is the email for this account
type: string
externalAccountBinding:
description: ExternalAcccountBinding is a reference to a CA external
account of the ACME server.
type: object
required:
- keyAlgorithm
- keyID
- keySecretRef
properties:
keyAlgorithm:
description: keyAlgorithm is the MAC key algorithm that the
key is used for. Valid values are "HS256", "HS384" and "HS512".
type: string
keyID:
description: keyID is the ID of the CA key that the External
Account is bound to.
type: string
keySecretRef:
description: keySecretRef is a Secret Key Selector referencing
a data item in a Kubernetes Secret which holds the symmetric
MAC key of the External Account Binding. The `key` is the
index string that is paired with the key data in the Secret
and should not be confused with the key data itself, or indeed
with the External Account Binding keyID above. The secret
key stored in the Secret **must** be un-padded, base64 URL
encoded data.
type: object
required:
- name
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
privateKeySecretRef:
description: PrivateKey is the name of a secret containing the private
key for this user account.

View File

@ -59,6 +59,44 @@ spec:
email:
description: Email is the email for this account
type: string
externalAccountBinding:
description: ExternalAcccountBinding is a reference to a CA external
account of the ACME server.
type: object
required:
- keyAlgorithm
- keyID
- keySecretRef
properties:
keyAlgorithm:
description: keyAlgorithm is the MAC key algorithm that the
key is used for. Valid values are "HS256", "HS384" and "HS512".
type: string
keyID:
description: keyID is the ID of the CA key that the External
Account is bound to.
type: string
keySecretRef:
description: keySecretRef is a Secret Key Selector referencing
a data item in a Kubernetes Secret which holds the symmetric
MAC key of the External Account Binding. The `key` is the
index string that is paired with the key data in the Secret
and should not be confused with the key data itself, or indeed
with the External Account Binding keyID above. The secret
key stored in the Secret **must** be un-padded, base64 URL
encoded data.
type: object
required:
- name
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
privateKeySecretRef:
description: PrivateKey is the name of a secret containing the private
key for this user account.

View File

@ -1886,6 +1886,44 @@ spec:
email:
description: Email is the email for this account
type: string
externalAccountBinding:
description: ExternalAcccountBinding is a reference to a CA external
account of the ACME server.
type: object
required:
- keyAlgorithm
- keyID
- keySecretRef
properties:
keyAlgorithm:
description: keyAlgorithm is the MAC key algorithm that the
key is used for. Valid values are "HS256", "HS384" and "HS512".
type: string
keyID:
description: keyID is the ID of the CA key that the External
Account is bound to.
type: string
keySecretRef:
description: keySecretRef is a Secret Key Selector referencing
a data item in a Kubernetes Secret which holds the symmetric
MAC key of the External Account Binding. The `key` is the
index string that is paired with the key data in the Secret
and should not be confused with the key data itself, or indeed
with the External Account Binding keyID above. The secret
key stored in the Secret **must** be un-padded, base64 URL
encoded data.
type: object
required:
- name
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
privateKeySecretRef:
description: PrivateKey is the name of a secret containing the private
key for this user account.
@ -3584,6 +3622,44 @@ spec:
email:
description: Email is the email for this account
type: string
externalAccountBinding:
description: ExternalAcccountBinding is a reference to a CA external
account of the ACME server.
type: object
required:
- keyAlgorithm
- keyID
- keySecretRef
properties:
keyAlgorithm:
description: keyAlgorithm is the MAC key algorithm that the
key is used for. Valid values are "HS256", "HS384" and "HS512".
type: string
keyID:
description: keyID is the ID of the CA key that the External
Account is bound to.
type: string
keySecretRef:
description: keySecretRef is a Secret Key Selector referencing
a data item in a Kubernetes Secret which holds the symmetric
MAC key of the External Account Binding. The `key` is the
index string that is paired with the key data in the Secret
and should not be confused with the key data itself, or indeed
with the External Account Binding keyID above. The secret
key stored in the Secret **must** be un-padded, base64 URL
encoded data.
type: object
required:
- name
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
privateKeySecretRef:
description: PrivateKey is the name of a secret containing the private
key for this user account.

View File

@ -36,6 +36,11 @@ type ACMEIssuer struct {
// +optional
SkipTLSVerify bool `json:"skipTLSVerify,omitempty"`
// ExternalAcccountBinding is a reference to a CA external account of the ACME
// server.
// +optional
ExternalAccountBinding *ACMEExternalAccountBinding `json:"externalAccountBinding,omitempty"`
// PrivateKey is the name of a secret containing the private key for this
// user account.
PrivateKey cmmeta.SecretKeySelector `json:"privateKeySecretRef"`
@ -46,6 +51,26 @@ type ACMEIssuer struct {
Solvers []ACMEChallengeSolver `json:"solvers,omitempty"`
}
// ACMEExternalAcccountBinding is a reference to a CA external account of the ACME
// server.
type ACMEExternalAccountBinding struct {
// keyID is the ID of the CA key that the External Account is bound to.
KeyID string `json:"keyID"`
// keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes
// Secret which holds the symmetric MAC key of the External Account Binding.
// The `key` is the index string that is paired with the key data in the
// Secret and should not be confused with the key data itself, or indeed with
// the External Account Binding keyID above.
// The secret key stored in the Secret **must** be un-padded, base64 URL
// encoded data.
Key cmmeta.SecretKeySelector `json:"keySecretRef"`
// keyAlgorithm is the MAC key algorithm that the key is used for. Valid
// values are "HS256", "HS384" and "HS512".
KeyAlgorithm string `json:"keyAlgorithm"`
}
type ACMEChallengeSolver struct {
// Selector selects a set of DNSNames on the Certificate resource that
// should be solved using this challenge solver.

View File

@ -291,9 +291,31 @@ func (in *ACMEChallengeSolverHTTP01IngressPodTemplate) DeepCopy() *ACMEChallenge
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
*out = *in
out.Key = in.Key
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEExternalAccountBinding.
func (in *ACMEExternalAccountBinding) DeepCopy() *ACMEExternalAccountBinding {
if in == nil {
return nil
}
out := new(ACMEExternalAccountBinding)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuer) DeepCopyInto(out *ACMEIssuer) {
*out = *in
if in.ExternalAccountBinding != nil {
in, out := &in.ExternalAccountBinding, &out.ExternalAccountBinding
*out = new(ACMEExternalAccountBinding)
**out = **in
}
out.PrivateKey = in.PrivateKey
if in.Solvers != nil {
in, out := &in.Solvers, &out.Solvers

View File

@ -36,6 +36,11 @@ type ACMEIssuer struct {
// +optional
SkipTLSVerify bool `json:"skipTLSVerify,omitempty"`
// ExternalAcccountBinding is a reference to a CA external account of the ACME
// server.
// +optional
ExternalAccountBinding *ACMEExternalAccountBinding `json:"externalAccountBinding,omitempty"`
// PrivateKey is the name of a secret containing the private key for this
// user account.
PrivateKey cmmeta.SecretKeySelector `json:"privateKeySecretRef"`
@ -46,6 +51,24 @@ type ACMEIssuer struct {
Solvers []ACMEChallengeSolver `json:"solvers,omitempty"`
}
// ACMEExternalAcccountBinding is a reference to a CA external account of the ACME
// server.
type ACMEExternalAccountBinding struct {
// keyID is the ID of the CA key that the External Account is bound to.
KeyID string `json:"keyID"`
// keySecretRef is a Secret Key Selector referencing a data item in a Kubernetes
// Secret which holds the symmetric MAC key of the External Account Binding.
// The `key` is the index string that is paired with the key data in the
// Secret and should not be confused with the key data itself, or indeed with
// the External Account Binding keyID above.
Key cmmeta.SecretKeySelector `json:"keySecretRef"`
// keyAlgorithm is the MAC key algorithm that the key is used for. Valid
// values are "HS256", "HS384" and "HS512".
KeyAlgorithm string `json:"keyAlgorithm"`
}
type ACMEChallengeSolver struct {
// Selector selects a set of DNSNames on the Certificate resource that
// should be solved using this challenge solver.

View File

@ -131,6 +131,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.ACMEExternalAccountBinding)(nil), (*acme.ACMEExternalAccountBinding)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(a.(*v1alpha2.ACMEExternalAccountBinding), b.(*acme.ACMEExternalAccountBinding), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*acme.ACMEExternalAccountBinding)(nil), (*v1alpha2.ACMEExternalAccountBinding)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_acme_ACMEExternalAccountBinding_To_v1alpha2_ACMEExternalAccountBinding(a.(*acme.ACMEExternalAccountBinding), b.(*v1alpha2.ACMEExternalAccountBinding), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha2.ACMEIssuer)(nil), (*acme.ACMEIssuer)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_ACMEIssuer_To_acme_ACMEIssuer(a.(*v1alpha2.ACMEIssuer), b.(*acme.ACMEIssuer), scope)
}); err != nil {
@ -568,10 +578,41 @@ func Convert_acme_ACMEChallengeSolverHTTP01IngressPodTemplate_To_v1alpha2_ACMECh
return autoConvert_acme_ACMEChallengeSolverHTTP01IngressPodTemplate_To_v1alpha2_ACMEChallengeSolverHTTP01IngressPodTemplate(in, out, s)
}
func autoConvert_v1alpha2_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(in *v1alpha2.ACMEExternalAccountBinding, out *acme.ACMEExternalAccountBinding, s conversion.Scope) error {
out.KeyID = in.KeyID
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.Key, &out.Key, 0); err != nil {
return err
}
out.KeyAlgorithm = in.KeyAlgorithm
return nil
}
// Convert_v1alpha2_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding is an autogenerated conversion function.
func Convert_v1alpha2_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(in *v1alpha2.ACMEExternalAccountBinding, out *acme.ACMEExternalAccountBinding, s conversion.Scope) error {
return autoConvert_v1alpha2_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(in, out, s)
}
func autoConvert_acme_ACMEExternalAccountBinding_To_v1alpha2_ACMEExternalAccountBinding(in *acme.ACMEExternalAccountBinding, out *v1alpha2.ACMEExternalAccountBinding, s conversion.Scope) error {
out.KeyID = in.KeyID
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.Key, &out.Key, 0); err != nil {
return err
}
out.KeyAlgorithm = in.KeyAlgorithm
return nil
}
// Convert_acme_ACMEExternalAccountBinding_To_v1alpha2_ACMEExternalAccountBinding is an autogenerated conversion function.
func Convert_acme_ACMEExternalAccountBinding_To_v1alpha2_ACMEExternalAccountBinding(in *acme.ACMEExternalAccountBinding, out *v1alpha2.ACMEExternalAccountBinding, s conversion.Scope) error {
return autoConvert_acme_ACMEExternalAccountBinding_To_v1alpha2_ACMEExternalAccountBinding(in, out, s)
}
func autoConvert_v1alpha2_ACMEIssuer_To_acme_ACMEIssuer(in *v1alpha2.ACMEIssuer, out *acme.ACMEIssuer, s conversion.Scope) error {
out.Email = in.Email
out.Server = in.Server
out.SkipTLSVerify = in.SkipTLSVerify
out.ExternalAccountBinding = (*acme.ACMEExternalAccountBinding)(unsafe.Pointer(in.ExternalAccountBinding))
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.PrivateKey, &out.PrivateKey, 0); err != nil {
return err
@ -589,6 +630,7 @@ func autoConvert_acme_ACMEIssuer_To_v1alpha2_ACMEIssuer(in *acme.ACMEIssuer, out
out.Email = in.Email
out.Server = in.Server
out.SkipTLSVerify = in.SkipTLSVerify
out.ExternalAccountBinding = (*v1alpha2.ACMEExternalAccountBinding)(unsafe.Pointer(in.ExternalAccountBinding))
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.PrivateKey, &out.PrivateKey, 0); err != nil {
return err

View File

@ -291,9 +291,31 @@ func (in *ACMEChallengeSolverHTTP01IngressPodTemplate) DeepCopy() *ACMEChallenge
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
*out = *in
out.Key = in.Key
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEExternalAccountBinding.
func (in *ACMEExternalAccountBinding) DeepCopy() *ACMEExternalAccountBinding {
if in == nil {
return nil
}
out := new(ACMEExternalAccountBinding)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuer) DeepCopyInto(out *ACMEIssuer) {
*out = *in
if in.ExternalAccountBinding != nil {
in, out := &in.ExternalAccountBinding, &out.ExternalAccountBinding
*out = new(ACMEExternalAccountBinding)
**out = **in
}
out.PrivateKey = in.PrivateKey
if in.Solvers != nil {
in, out := &in.Solvers, &out.Solvers

View File

@ -103,6 +103,20 @@ func ValidateACMEIssuerConfig(iss *cmacme.ACMEIssuer, fldPath *field.Path) field
if len(iss.Server) == 0 {
el = append(el, field.Required(fldPath.Child("server"), "acme server URL is a required field"))
}
if eab := iss.ExternalAccountBinding; eab != nil {
eabFldPath := fldPath.Child("externalAccountBinding")
if len(eab.KeyID) == 0 {
el = append(el, field.Required(eabFldPath.Child("keyID"), "the keyID field is required when using externalAccountBinding"))
}
el = append(el, ValidateSecretKeySelector(&eab.Key, eabFldPath.Child("keySecretRef"))...)
if len(eab.KeyAlgorithm) == 0 {
el = append(el, field.Required(eabFldPath.Child("keyAlgorithm"), "the keyAlgorithm field is required when using externalAccountBinding"))
}
}
for i, sol := range iss.Solvers {
el = append(el, ValidateACMEIssuerChallengeSolverConfig(&sol, fldPath.Child("solvers").Index(i))...)
}

View File

@ -144,6 +144,27 @@ func TestValidateACMEIssuerConfig(t *testing.T) {
},
},
},
"acme solver with empty external account binding fields": {
spec: &cmacme.ACMEIssuer{
Email: "valid-email",
Server: "valid-server",
PrivateKey: validSecretKeyRef,
ExternalAccountBinding: &cmacme.ACMEExternalAccountBinding{},
Solvers: []cmacme.ACMEChallengeSolver{
{
DNS01: &cmacme.ACMEChallengeSolverDNS01{
CloudDNS: &validCloudDNSProvider,
},
},
},
},
errs: []*field.Error{
field.Required(fldPath.Child("externalAccountBinding.keyID"), "the keyID field is required when using externalAccountBinding"),
field.Required(fldPath.Child("externalAccountBinding.keySecretRef.name"), "secret name is required"),
field.Required(fldPath.Child("externalAccountBinding.keySecretRef.key"), "secret key is required"),
field.Required(fldPath.Child("externalAccountBinding.keyAlgorithm"), "the keyAlgorithm field is required when using externalAccountBinding"),
},
},
"acme solver with missing http01 config type": {
spec: &cmacme.ACMEIssuer{
Email: "valid-email",