Repository: cert-manager/cert-manager
Pull request #7081: Fix "IP only" certificates with IP in common name
Author: login: johnjcool
Labels: name: release-note
description: Denotes a PR that will be considered when it comes time to generate release notes.
name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: lgtm
description: Indicates that a PR is ready to be merged.
name: kind/bug
description: Categorizes issue or PR as related to a bug.
name: size/M
description: Denotes a PR that changes 30-99 lines, ignoring generated files.
name: area/acme
description: Indicates a PR directly modifies the ACME Issuer code
name: dco-signoff: yes
description: Indicates that all commits in the pull request have the valid DCO sign-off message.
name: ok-to-test
Created: 2024-06-07T14:32:31Z | Merged: 2025-04-21T20:41:36Z

### Pull Request Motivation

this pr fixes the issue if you need to use an ip address as common name in subject literal in combination with acme protocol if you only use ip addresses. 

csr example:
Subject: C=us, O=organization, OU=unit, CN=1.2.3.4
X509v3 Subject Alternative Name: 
  IP Address:1.2.3.4

the common name has to be validated if it is an ip or a dns name and than put it in the right identifierSet instead always to type dns.

before change acme payload --> broken 400 urn:ietf:params:acme:error:badCSR:
{"identifiers":[{"type":"dns","value":"1.2.3.4"},{"type":"ip","value":"1.2.3.4"}]}

after change acme payload --> ok:
{"identifiers": [{"type":"ip","value":"1.2.3.4"}]}

### Kind
bug

### Release Note

```release-note
Fix handling of certificates with IP addresses in the `commonName` field; IP addresses are no longer added to the DNS `subjectAlternativeName` list and are instead added to the `ipAddresses` field as expected.
```



Diff:
diff --git a/pkg/controller/acmeorders/sync.go b/pkg/controller/acmeorders/sync.go
index 2af09daa948..017b325cc1d 100644
--- a/pkg/controller/acmeorders/sync.go
+++ b/pkg/controller/acmeorders/sync.go
@@ -22,6 +22,7 @@ import (
 	"crypto/x509"
 	"encoding/pem"
 	"fmt"
+	"net"
 	"net/http"
 	"time"
 
@@ -34,6 +35,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 	"k8s.io/apimachinery/pkg/util/sets"
+	"k8s.io/apimachinery/pkg/util/validation"
 
 	"github.com/cert-manager/cert-manager/internal/controller/feature"
 	internalorders "github.com/cert-manager/cert-manager/internal/controller/orders"
@@ -265,13 +267,15 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
+	ipIdentifierSet := sets.New[string](o.Spec.IPAddresses...)
+	switch {
+	case o.Spec.CommonName == "":
+	case net.ParseIP(o.Spec.CommonName) != nil:
+		ipIdentifierSet.Insert(o.Spec.CommonName)
+	case len(validation.IsFullyQualifiedDomainName(nil, o.Spec.CommonName)) == 0:
 		dnsIdentifierSet.Insert(o.Spec.CommonName)
 	}
-	log.V(logf.DebugLevel).Info("build set of domains for Order", "domains", sets.List(dnsIdentifierSet))
-
-	ipIdentifierSet := sets.New[string](o.Spec.IPAddresses...)
-	log.V(logf.DebugLevel).Info("build set of IPs for Order", "domains", sets.List(dnsIdentifierSet))
+	log.V(logf.DebugLevel).Info("built set of identifiers for Order", "domains", sets.List(dnsIdentifierSet), "ipAddresses", sets.List(ipIdentifierSet))
 
 	authzIDs := acmeapi.DomainIDs(sets.List(dnsIdentifierSet)...)
 	authzIDs = append(authzIDs, acmeapi.IPIDs(sets.List(ipIdentifierSet)...)...)
diff --git a/pkg/controller/acmeorders/sync_test.go b/pkg/controller/acmeorders/sync_test.go
index 237de09537c..4ba142f5c43 100644
--- a/pkg/controller/acmeorders/sync_test.go
+++ b/pkg/controller/acmeorders/sync_test.go
@@ -90,7 +90,22 @@ func TestSync(t *testing.T) {
 		}),
 	)
 
-	testOrderIP := gen.Order("testorder", gen.SetOrderIssuer(cmmeta.ObjectReference{Name: testIssuerHTTP01.Name}), gen.SetOrderIPAddresses("10.0.0.1"))
+	testOrderIP := gen.Order("testorder",
+		gen.SetOrderCommonName("10.0.0.2"),
+		gen.SetOrderIssuer(cmmeta.ObjectReference{
+			Name: testIssuerHTTP01.Name,
+		}),
+		gen.SetOrderIPAddresses("10.0.0.1"))
+
+	const ipv6AddressOne = "2001:4860:4860::8888"
+	const ipv6AddressTwo = "2001:4860:4860::8844"
+
+	testOrderIPV6 := gen.Order("testorder",
+		gen.SetOrderCommonName(ipv6AddressOne),
+		gen.SetOrderIssuer(cmmeta.ObjectReference{
+			Name: testIssuerHTTP01.Name,
+		}),
+		gen.SetOrderIPAddresses(ipv6AddressTwo))
 
 	pendingStatus := cmacme.OrderStatus{
 		State:       cmacme.Pending,
@@ -381,7 +396,7 @@ Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
 				},
 			},
 		},
-		"create a new order with the acme server with an IP address": {
+		"create a new order with the acme server with an IPv4 address": {
 			order: testOrderIP,
 			builder: &testpkg.Builder{
 				CertManagerObjects: []runtime.Object{testIssuerHTTP01, testOrderIP},
@@ -406,6 +421,53 @@ Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
 					if id[0].Value != "10.0.0.1" || id[0].Type != "ip" {
 						return nil, errors.New("AuthzID needs to be the IP")
 					}
+					if id[1].Value != "10.0.0.2" || id[1].Type != "ip" {
+						return nil, errors.New("AuthzID needs to be the IP")
+					}
+					return testACMEOrderPending, nil
+				},
+				FakeGetAuthorization: func(ctx context.Context, url string) (*acmeapi.Authorization, error) {
+					if url != "http://authzurl" {
+						return nil, fmt.Errorf("Invalid URL: expected http://authzurl got %q", url)
+					}
+					return testACMEAuthorizationPending, nil
+				},
+				FakeHTTP01ChallengeResponse: func(s string) (string, error) {
+					// TODO: assert s = "token"
+					return "key", nil
+				},
+			},
+		},
+		"create a new order with the acme server with an IPv6 address": {
+			order: testOrderIPV6,
+			builder: &testpkg.Builder{
+				CertManagerObjects: []runtime.Object{testIssuerHTTP01, testOrderIPV6},
+				ExpectedActions: []testpkg.Action{
+					testpkg.NewAction(coretesting.NewUpdateSubresourceAction(cmacme.SchemeGroupVersion.WithResource("orders"),
+						"status",
+						testOrderPending.Namespace,
+						gen.OrderFrom(testOrderIPV6, gen.SetOrderStatus(cmacme.OrderStatus{
+							State:       cmacme.Pending,
+							URL:         "http://testurl.com/abcde",
+							FinalizeURL: "http://testurl.com/abcde/finalize",
+							Authorizations: []cmacme.ACMEAuthorization{
+								{
+									URL: "http://authzurl",
+								},
+							},
+						})))),
+				},
+			},
+			acmeClient: &acmecl.FakeACME{
+				FakeAuthorizeOrder: func(ctx context.Context, id []acmeapi.AuthzID, opt ...acmeapi.OrderOption) (*acmeapi.Order, error) {
+					if id[0].Value != ipv6AddressTwo || id[0].Type != "ip" {
+						return nil, fmt.Errorf("AuthzID 1 needs to be expected IPv6 address: wanted value=%s but got %s", ipv6AddressTwo, id[0].Value)
+					}
+
+					if id[1].Value != ipv6AddressOne || id[1].Type != "ip" {
+						return nil, fmt.Errorf("AuthzID 2 needs to be expected IPv6 address: wanted value=%s but got %s", ipv6AddressOne, id[1].Value)
+					}
+
 					return testACMEOrderPending, nil
 				},
 				FakeGetAuthorization: func(ctx context.Context, url string) (*acmeapi.Authorization, error) {


Reviews:
user: login: SgtCoDFish
body: @erikgb pointed me at this at KubeCon - thank you for raising this and thank you for your patience. I've got a question!
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-10T11:26:46Z
user: login: TobsenD
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-10T14:00:35Z
user: login: erikgb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-10T14:19:30Z
user: login: johnjcool
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T08:40:03Z
user: login: johnjcool
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T08:42:25Z
user: login: TobsenD
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-11T08:42:46Z
user: login: SgtCoDFish
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T10:58:08Z
user: login: erikgb
body: Thanks @johnjcool! Looks promising to me, but I would like to see a test that shows this change fixes your problem. And @SgtCoDFish is more fluent in specifications than me. 😉 
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T23:21:14Z
user: login: erikgb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-12T14:33:27Z
user: login: SgtCoDFish
body: Sorry, two last little bits. I think this is super close to being ready to merge!
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-16T09:08:31Z
user: login: johnjcool
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T10:28:20Z

Review comments:
diff_hunk: @@ -268,9 +268,6 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
-		dnsIdentifierSet.Insert(o.Spec.CommonName)
-	}
path: pkg/controller/acmeorders/sync.go
user: login: SgtCoDFish
body: question: Bear with me, I'm sick right now! Would removing this not create the issue that a common name might not appear in the DNS SAN extension, which it should do?

I.e.  a cert with CN `example.com` and DNSNames of `hello.example.com` - that cert _should_ be issued with the CN included in the SAN extension.

I guess what I'm wondering is: Should be checking if the common name is a valid DNS name, and if so adding it to dnsIdentifierSet? E.g.

```go
	if isValidDNSName(o.Spec.CommonName) {
		dnsIdentifierSet.Insert(o.Spec.CommonName)
	}
```

(there'll be a function for checking isValidDNSName, somewhere)
created_at: 2025-04-10T11:26:10Z
author_association: MEMBER
diff_hunk: @@ -268,9 +268,6 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
-		dnsIdentifierSet.Insert(o.Spec.CommonName)
-	}
path: pkg/controller/acmeorders/sync.go
user: login: TobsenD
body: Hi @SgtCoDFish thank you for the reply.

Your remarks would indeed be a good solution. The quick solution in this case would be to remove the common name. However, you are right it should not really be missing, but validated.
 
As I understand it, isValidDNSName is supposed to check the schema of the common name. Probably using a regex.
 
I'm no expert here, so this should definitely be cross-checked. Wiki says **RFC 1034** and **RFC 1035** must be applied.
 
According to these standards, DNS names consist of labels separated by periods `('.')`, where each label can contain between 1 and 63 characters. The entire domain should not exceed 253 characters. Labels may only contain letters `('a-z', 'A-Z')`, digits `('0-9')`, and hyphens `('-')`, may not start or end with a hyphen, upper and lowercase are treated the same and may not be empty.
 
The Regular expression to follow these rules would be
`^(?=.{1,253}$)(^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[A-Za-z]{2,}$)$`
 
### Explanation:
#### Length check:
`(?=.{1,253}$)`: Ensures that the entire domain does not exceed 253 characters.
 
#### Label check:
`^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+`: Each label starts with an alphanumeric character, followed by 0 to 61 additional characters (letters, numbers, or hyphens), and ends with an alphanumeric character. Labels are separated by periods.
The pattern accounts for multiple labels in the domain name.
 
#### Top-level domain check:
`[A-Za-z]{2,}$`: Ensures that the last label (TLD) is at least two characters long and contains only letters.
 
This ensures that common names like
 
```
example.com
sub.example.org
sub-example.example.com
sub-example.example.example.example.example.example.com
valid123.example-domain.net
abcdefghiyklmnopqrstuvwxyzabcdefghiyklmnopqrstuvwxyzabcdefghijk.example.com
```
 
are valid, and entries like
 
```
-invalid.example.com
invalid-.example.com
192.168.0.1
abcdefghiyklmnopqrstuvwxyzabcdefghiyklmnopqrstuvwxyzabcdefghijkl.example.com
```
 
would be invalid.

![image](https://github.com/user-attachments/assets/cc10443e-71ae-4ae7-961c-115c2332cf0e)

 
This method must then be written.
created_at: 2025-04-10T14:00:35Z
author_association: NONE
diff_hunk: @@ -268,9 +268,6 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
-		dnsIdentifierSet.Insert(o.Spec.CommonName)
-	}
path: pkg/controller/acmeorders/sync.go
user: login: erikgb
body: I am pretty sure we can use one of the [functions provided by kube api-machinery](https://github.com/kubernetes/apimachinery/blob/master/pkg/util/validation/validation.go). But the question is which function validates this correctly?
created_at: 2025-04-10T14:19:30Z
author_association: MEMBER
diff_hunk: @@ -268,9 +268,6 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
-		dnsIdentifierSet.Insert(o.Spec.CommonName)
-	}
path: pkg/controller/acmeorders/sync.go
user: login: johnjcool
body: hi,
copilot suggests this:

```go

package main

import (
    "fmt"
    "regexp"
    "k8s.io/apimachinery/pkg/util/validation"
)

// ValidateDNSName validates a DNS name using Kubernetes API Machinery
func ValidateDNSName(dnsName string) error {
    // Check if the name is a valid DNS subdomain
    if errs := validation.IsDNS1123Subdomain(dnsName); len(errs) > 0 {
        return fmt.Errorf("invalid DNS name: %v", errs)
    }

    // Regular expression to match IP addresses
    ipRegex := `^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`
    if matched, _ := regexp.MatchString(ipRegex, dnsName); matched {
        return fmt.Errorf("invalid DNS name: IP addresses are not allowed")
    }

    return nil
}

func main() {
    dnsName := "example.com"
    if err := ValidateDNSName(dnsName); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("Valid DNS name")
    }
}
```
This function first checks if the DNS name conforms to Kubernetes DNS subdomain rules. Then, it uses a regular expression to check if the name is an IP address. If it is, an error is returned.

created_at: 2025-04-11T08:40:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -268,9 +268,6 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
-		dnsIdentifierSet.Insert(o.Spec.CommonName)
-	}
path: pkg/controller/acmeorders/sync.go
user: login: johnjcool
body: looking at the tests, an ip is a valid dns name. so in this case it must be a combination of dns and ip...

first check: IsFullyQualifiedDomainName -> https://github.com/kubernetes/apimachinery/blob/7b4292bd2e85a1c85d8a7a6225535c433d24150c/pkg/util/validation/validation_test.go#L447

second check: IsValidIP -> https://github.com/kubernetes/apimachinery/blob/master/pkg/util/validation/ip_test.go#L216
created_at: 2025-04-11T08:42:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -268,9 +268,6 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
-		dnsIdentifierSet.Insert(o.Spec.CommonName)
-	}
path: pkg/controller/acmeorders/sync.go
user: login: TobsenD
body: Maybe its simple with IsFullyQualifiedDomainName https://github.com/kubernetes/apimachinery/blob/7b4292bd2e85a1c85d8a7a6225535c433d24150c/pkg/util/validation/validation.go#L94 

but there is also the IP included https://github.com/kubernetes/apimachinery/blob/7b4292bd2e85a1c85d8a7a6225535c433d24150c/pkg/util/validation/validation_test.go#L456
created_at: 2025-04-11T08:42:46Z
author_association: NONE
diff_hunk: @@ -268,9 +268,6 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
-		dnsIdentifierSet.Insert(o.Spec.CommonName)
-	}
path: pkg/controller/acmeorders/sync.go
user: login: SgtCoDFish
body: I think what we're trying to achieve with the bit of code that changed here is adherence to this requirement from the [CAB forum](https://cabforum.org/working-groups/server/baseline-requirements/documents/CA-Browser-Forum-TLS-BR-2.1.4.pdf):

> If present, [the CommonName] attribute MUST contain exactly one entry that is one of the values contained in the Certificate’s subjectAltName extension

So, I guess the checks would be:

If commonName IsValidIP -> add to ipaddresses SAN
else if commonName IsValidDNS -> add to dnsNames SAN

EDIT: It's true that an IP (specifically IPv4) address can look like a valid DNS name, but the encoding of DNS names vs IP addresses is different internally in a certificate, so we should use the ipAddresses SAN in preference to the DNS names SAN
created_at: 2025-04-11T10:58:08Z
author_association: MEMBER
diff_hunk: @@ -265,13 +267,14 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
+	ipIdentifierSet := sets.New[string](o.Spec.IPAddresses...)
+	if len(validation.IsValidIP(field.NewPath(""), o.Spec.CommonName)) == 0 {
+		ipIdentifierSet.Insert(o.Spec.CommonName)
+	} else if len(validation.IsFullyQualifiedDomainName(field.NewPath(""), o.Spec.CommonName)) == 0 {
 		dnsIdentifierSet.Insert(o.Spec.CommonName)
 	}
path: pkg/controller/acmeorders/sync.go
user: login: erikgb
body: ```suggestion
	switch {
	case o.Spec.CommonName == "":
	case net.ParseIP(o.Spec.CommonName) != nil:
		ipIdentifierSet.Insert(o.Spec.CommonName)
	case len(validation.IsFullyQualifiedDomainName(nil, o.Spec.CommonName)) == 0:
		dnsIdentifierSet.Insert(o.Spec.CommonName)
	}
```

WDYT of using a switch instead? I find it easier to read, and we should also keep the early exit if CN is not set.

And I think we can use the Go stdlib function to check for IP instead of the apimachinery wrapper function.
created_at: 2025-04-11T23:19:25Z
author_association: MEMBER
diff_hunk: @@ -90,7 +90,12 @@ func TestSync(t *testing.T) {
 		}),
 	)
 
-	testOrderIP := gen.Order("testorder", gen.SetOrderIssuer(cmmeta.ObjectReference{Name: testIssuerHTTP01.Name}), gen.SetOrderIPAddresses("10.0.0.1"))
+	testOrderIP := gen.Order("testorder",
+		gen.SetOrderCommonName("10.0.0.2"),
path: pkg/controller/acmeorders/sync_test.go
user: login: erikgb
body: Maybe also assert that the IP from CN is added to the IPs? I am not too familiar with the test structure here, but it seems like it should be possible around here:

https://github.com/cert-manager/cert-manager/blob/833e97bdf2285403a2a876d6c4d0a9c6b9a4261c/pkg/controller/acmeorders/sync_test.go#L411-L413
created_at: 2025-04-12T14:33:26Z
author_association: MEMBER
diff_hunk: @@ -265,13 +267,16 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
 	log.V(logf.DebugLevel).Info("order URL not set, submitting Order to ACME server")
 
 	dnsIdentifierSet := sets.New[string](o.Spec.DNSNames...)
-	if o.Spec.CommonName != "" {
+	ipIdentifierSet := sets.New[string](o.Spec.IPAddresses...)
+	switch {
+	case o.Spec.CommonName == "":
+	case net.ParseIP(o.Spec.CommonName) != nil:
+		ipIdentifierSet.Insert(o.Spec.CommonName)
+	case len(validation.IsFullyQualifiedDomainName(nil, o.Spec.CommonName)) == 0:
 		dnsIdentifierSet.Insert(o.Spec.CommonName)
 	}
 	log.V(logf.DebugLevel).Info("build set of domains for Order", "domains", sets.List(dnsIdentifierSet))
-
-	ipIdentifierSet := sets.New[string](o.Spec.IPAddresses...)
-	log.V(logf.DebugLevel).Info("build set of IPs for Order", "domains", sets.List(dnsIdentifierSet))
+	log.V(logf.DebugLevel).Info("build set of IPs for Order", "domains", sets.List(ipIdentifierSet))
path: pkg/controller/acmeorders/sync.go
user: login: SgtCoDFish
body: nitpick: I think this could be one log line, allowing us to fix the broken English in the existing message and save a log line

```go
log.V(logf.DebugLevel).Info("built set of identifiers for Order", "domains", sets.List(dnsIdentifierSet), "ipAddresses", sets.List(ipIdentifierSet))
```
created_at: 2025-04-16T09:06:50Z
author_association: MEMBER
diff_hunk: @@ -90,7 +90,12 @@ func TestSync(t *testing.T) {
 		}),
 	)
 
-	testOrderIP := gen.Order("testorder", gen.SetOrderIssuer(cmmeta.ObjectReference{Name: testIssuerHTTP01.Name}), gen.SetOrderIPAddresses("10.0.0.1"))
+	testOrderIP := gen.Order("testorder",
+		gen.SetOrderCommonName("10.0.0.2"),
path: pkg/controller/acmeorders/sync_test.go
user: login: SgtCoDFish
body: suggestion: could we also add a test for IPv6? e.g. what happens if we have `2001:4860:4860::8844` in the common name?
created_at: 2025-04-16T09:08:06Z
author_association: MEMBER
diff_hunk: @@ -90,7 +90,12 @@ func TestSync(t *testing.T) {
 		}),
 	)
 
-	testOrderIP := gen.Order("testorder", gen.SetOrderIssuer(cmmeta.ObjectReference{Name: testIssuerHTTP01.Name}), gen.SetOrderIPAddresses("10.0.0.1"))
+	testOrderIP := gen.Order("testorder",
+		gen.SetOrderCommonName("10.0.0.2"),
path: pkg/controller/acmeorders/sync_test.go
user: login: johnjcool
body: Sure, but I'm not at all sure about the test code. The test should be added by someone who is familiar with it.
created_at: 2025-04-16T10:28:20Z
author_association: CONTRIBUTOR

PR comments:
user: login: cert-manager-prow[bot]
created_at: 2024-06-07T14:32:42Z
body: Hi @johnjcool. Thanks for your PR.

I'm waiting for a [cert-manager](https://github.com/orgs/cert-manager/people) 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](https://github.com/orgs/cert-manager/people) 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](https://go.k8s.io/bot-commands?repo=cert-manager%2Fcert-manager).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>

author_association: CONTRIBUTOR
user: login: johnjcool
created_at: 2024-06-24T09:44:03Z
body: are you alive?
author_association: CONTRIBUTOR
user: login: jsoref
created_at: 2024-09-17T13:35:55Z
body: /kind bug
author_association: CONTRIBUTOR
user: login: cert-manager-bot
created_at: 2024-12-16T14:30:06Z
body: Issues go stale after 90d of inactivity.
Mark the issue as fresh with `/remove-lifecycle stale`.
Stale issues rot after an additional 30d of inactivity and eventually close.
If this issue is safe to close now please do so with `/close`.
/lifecycle stale
author_association: CONTRIBUTOR
user: login: TobsenD
created_at: 2024-12-16T15:12:49Z
body: /remove-lifecycle stale
author_association: NONE
user: login: cert-manager-bot
created_at: 2025-03-16T15:38:38Z
body: Issues go stale after 90d of inactivity.
Mark the issue as fresh with `/remove-lifecycle stale`.
Stale issues rot after an additional 30d of inactivity and eventually close.
If this issue is safe to close now please do so with `/close`.
/lifecycle stale
author_association: CONTRIBUTOR
user: login: TobsenD
created_at: 2025-03-16T16:02:39Z
body: /remove-lifecycle stale
author_association: NONE
user: login: johnjcool
created_at: 2025-04-12T06:36:42Z
body: I have extended the testOrderIP test by the commonName with an IP. The result looked good in the debugger.

DNS commonName:
<img width="1314" alt="Pasted Graphic" src="https://github.com/user-attachments/assets/5969dc73-2e04-4f5e-9425-3aa07925b4d2" />

IP commonName:
<img width="1434" alt="Pasted Graphic 1" src="https://github.com/user-attachments/assets/19638817-ce5d-48ac-ba5d-6d01c7628b58" />
author_association: CONTRIBUTOR
user: login: johnjcool
created_at: 2025-04-12T06:38:14Z
body: Perhaps we should change the title of the issue.
author_association: CONTRIBUTOR
user: login: erikgb
created_at: 2025-04-12T14:16:53Z
body: > Perhaps we should change the title of the issue.

Yes, I think we should adjust both the title of the PR and the release notes entry.
author_association: MEMBER
user: login: erikgb
created_at: 2025-04-14T10:35:24Z
body: /ok-to-test
author_association: MEMBER
user: login: SgtCoDFish
created_at: 2025-04-17T10:30:00Z
body: I've pushed a test for IPv6 too. That means I can't LGTM this any more, so I'll ask someone else to review!
author_association: MEMBER
user: login: inteon
created_at: 2025-04-17T11:31:41Z
body: Thanks for these changes @johnjcool, LGTM!
Could you update the PR description and release notes? I think "removed unnecessary check" no longer applies.

/approve
/lgtm
/hold
author_association: MEMBER
user: login: cert-manager-prow[bot]
created_at: 2025-04-17T11:31:50Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/cert-manager/cert-manager/pull/7081#issuecomment-2812576279" title="LGTM">inteon</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=cert-manager%2Fcert-manager).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/cert-manager/cert-manager/blob/master/OWNERS)~~ [inteon]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: johnjcool
created_at: 2025-04-17T16:38:20Z
body: > Thanks for these changes @johnjcool, LGTM! Could you update the PR description and release notes? I think "removed unnecessary check" no longer applies.

done


author_association: CONTRIBUTOR
user: login: inteon
created_at: 2025-04-17T17:57:43Z
body: /unhold
author_association: MEMBER
user: login: wallrj
created_at: 2025-05-14T10:59:17Z
body: 📢 A new pre-release is available which contains this fix or feature: 
 * https://github.com/cert-manager/cert-manager/releases/tag/v1.18.0-alpha.0. 
 
Please test and report back. 
author_association: MEMBER