Add tests for IP Order validations

This commit is contained in:
Herman Slatman 2021-06-18 16:09:48 +02:00
parent db416a45ae
commit 218a2adb9f
No known key found for this signature in database
GPG key ID: F4D8A44EA0A75A4F
2 changed files with 380 additions and 7 deletions

View file

@ -199,15 +199,15 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
var sans []x509util.SubjectAlternativeName var sans []x509util.SubjectAlternativeName
// order the DNS names and IP addresses, so that they can be compared against the canonicalized CSR // order the DNS names and IP addresses, so that they can be compared against the canonicalized CSR
orderNames := make([]string, numberOfIdentifierType("dns", o.Identifiers)) orderNames := make([]string, numberOfIdentifierType(DNS, o.Identifiers))
orderIPs := make([]net.IP, numberOfIdentifierType("ip", o.Identifiers)) orderIPs := make([]net.IP, numberOfIdentifierType(IP, o.Identifiers))
indexDNS, indexIP := 0, 0 indexDNS, indexIP := 0, 0
for _, n := range o.Identifiers { for _, n := range o.Identifiers {
switch n.Type { switch n.Type {
case "dns": case DNS:
orderNames[indexDNS] = n.Value orderNames[indexDNS] = n.Value
indexDNS++ indexDNS++
case "ip": case IP:
orderIPs[indexIP] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs at this time; or will result in nil entries orderIPs[indexIP] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs at this time; or will result in nil entries
indexIP++ indexIP++
default: default:
@ -303,8 +303,8 @@ func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.Certificate
} }
// ipsAreEqual compares IPs to be equal. IPv6 representations of IPv4 // ipsAreEqual compares IPs to be equal. IPv6 representations of IPv4
// adresses are NOT considered equal to the IPv4 address in this case. // adresses are considered equal to the IPv4 address in this case.
// Both IPs should be the same version AND equal to each other. // TODO: is this behavior OK to keep?
func ipsAreEqual(x, y net.IP) bool { func ipsAreEqual(x, y net.IP) bool {
if matchAddrFamily(x, y) { if matchAddrFamily(x, y) {
return x.Equal(y) return x.Equal(y)

View file

@ -393,6 +393,31 @@ func TestOrder_Finalize(t *testing.T) {
"CSR names = %v, Order names = %v", []string{"foo.internal"}, orderNames), "CSR names = %v, Order names = %v", []string{"foo.internal"}, orderNames),
} }
}, },
"fail/error-ips-length-mismatch": func(t *testing.T) test {
now := clock.Now()
o := &Order{
ID: "oID",
AccountID: "accID",
Status: StatusReady,
ExpiresAt: now.Add(5 * time.Minute),
AuthorizationIDs: []string{"a", "b"},
Identifiers: []Identifier{
{Type: "ip", Value: "192.168.42.42"},
{Type: "ip", Value: "192.168.43.42"},
},
}
orderIPs := []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}
csr := &x509.CertificateRequest{
IPAddresses: []net.IP{net.ParseIP("192.168.42.42")},
}
return test{
o: o,
csr: csr,
err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+
"CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.42")}, orderIPs),
}
},
"fail/error-names-mismatch": func(t *testing.T) test { "fail/error-names-mismatch": func(t *testing.T) test {
now := clock.Now() now := clock.Now()
o := &Order{ o := &Order{
@ -421,6 +446,31 @@ func TestOrder_Finalize(t *testing.T) {
"CSR names = %v, Order names = %v", []string{"foo.internal", "zap.internal"}, orderNames), "CSR names = %v, Order names = %v", []string{"foo.internal", "zap.internal"}, orderNames),
} }
}, },
"fail/error-ips-mismatch": func(t *testing.T) test {
now := clock.Now()
o := &Order{
ID: "oID",
AccountID: "accID",
Status: StatusReady,
ExpiresAt: now.Add(5 * time.Minute),
AuthorizationIDs: []string{"a", "b"},
Identifiers: []Identifier{
{Type: "ip", Value: "192.168.42.42"},
{Type: "ip", Value: "192.168.43.42"},
},
}
orderIPs := []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}
csr := &x509.CertificateRequest{
IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.32")},
}
return test{
o: o,
csr: csr,
err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+
"CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.32"), net.ParseIP("192.168.42.42")}, orderIPs),
}
},
"fail/error-provisioner-auth": func(t *testing.T) test { "fail/error-provisioner-auth": func(t *testing.T) test {
now := clock.Now() now := clock.Now()
o := &Order{ o := &Order{
@ -652,7 +702,7 @@ func TestOrder_Finalize(t *testing.T) {
err: NewErrorISE("error updating order oID: force"), err: NewErrorISE("error updating order oID: force"),
} }
}, },
"ok/new-cert": func(t *testing.T) test { "ok/new-cert-dns": func(t *testing.T) test {
now := clock.Now() now := clock.Now()
o := &Order{ o := &Order{
ID: "oID", ID: "oID",
@ -676,6 +726,131 @@ func TestOrder_Finalize(t *testing.T) {
bar := &x509.Certificate{Subject: pkix.Name{CommonName: "bar"}} bar := &x509.Certificate{Subject: pkix.Name{CommonName: "bar"}}
baz := &x509.Certificate{Subject: pkix.Name{CommonName: "baz"}} baz := &x509.Certificate{Subject: pkix.Name{CommonName: "baz"}}
return test{
o: o,
csr: csr,
prov: &MockProvisioner{
MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) {
assert.Equals(t, token, "")
return nil, nil
},
MgetOptions: func() *provisioner.Options {
return nil
},
},
ca: &mockSignAuth{
sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
assert.Equals(t, _csr, csr)
return []*x509.Certificate{foo, bar, baz}, nil
},
},
db: &MockDB{
MockCreateCertificate: func(ctx context.Context, cert *Certificate) error {
cert.ID = "certID"
assert.Equals(t, cert.AccountID, o.AccountID)
assert.Equals(t, cert.OrderID, o.ID)
assert.Equals(t, cert.Leaf, foo)
assert.Equals(t, cert.Intermediates, []*x509.Certificate{bar, baz})
return nil
},
MockUpdateOrder: func(ctx context.Context, updo *Order) error {
assert.Equals(t, updo.CertificateID, "certID")
assert.Equals(t, updo.Status, StatusValid)
assert.Equals(t, updo.ID, o.ID)
assert.Equals(t, updo.AccountID, o.AccountID)
assert.Equals(t, updo.ExpiresAt, o.ExpiresAt)
assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs)
assert.Equals(t, updo.Identifiers, o.Identifiers)
return nil
},
},
}
},
"ok/new-cert-ip": func(t *testing.T) test {
now := clock.Now()
o := &Order{
ID: "oID",
AccountID: "accID",
Status: StatusReady,
ExpiresAt: now.Add(5 * time.Minute),
AuthorizationIDs: []string{"a", "b"},
Identifiers: []Identifier{
{Type: "ip", Value: "192.168.42.42"},
{Type: "ip", Value: "192.168.43.42"},
},
}
csr := &x509.CertificateRequest{
IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}, // in case of IPs, no Common Name
}
foo := &x509.Certificate{Subject: pkix.Name{CommonName: "foo"}}
bar := &x509.Certificate{Subject: pkix.Name{CommonName: "bar"}}
baz := &x509.Certificate{Subject: pkix.Name{CommonName: "baz"}}
return test{
o: o,
csr: csr,
prov: &MockProvisioner{
MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) {
assert.Equals(t, token, "")
return nil, nil
},
MgetOptions: func() *provisioner.Options {
return nil
},
},
ca: &mockSignAuth{
sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
assert.Equals(t, _csr, csr)
return []*x509.Certificate{foo, bar, baz}, nil
},
},
db: &MockDB{
MockCreateCertificate: func(ctx context.Context, cert *Certificate) error {
cert.ID = "certID"
assert.Equals(t, cert.AccountID, o.AccountID)
assert.Equals(t, cert.OrderID, o.ID)
assert.Equals(t, cert.Leaf, foo)
assert.Equals(t, cert.Intermediates, []*x509.Certificate{bar, baz})
return nil
},
MockUpdateOrder: func(ctx context.Context, updo *Order) error {
assert.Equals(t, updo.CertificateID, "certID")
assert.Equals(t, updo.Status, StatusValid)
assert.Equals(t, updo.ID, o.ID)
assert.Equals(t, updo.AccountID, o.AccountID)
assert.Equals(t, updo.ExpiresAt, o.ExpiresAt)
assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs)
assert.Equals(t, updo.Identifiers, o.Identifiers)
return nil
},
},
}
},
"ok/new-cert-dns-and-ip": func(t *testing.T) test {
now := clock.Now()
o := &Order{
ID: "oID",
AccountID: "accID",
Status: StatusReady,
ExpiresAt: now.Add(5 * time.Minute),
AuthorizationIDs: []string{"a", "b"},
Identifiers: []Identifier{
{Type: "dns", Value: "foo.internal"},
{Type: "ip", Value: "192.168.42.42"},
},
}
csr := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "foo.internal",
},
IPAddresses: []net.IP{net.ParseIP("192.168.42.42")},
}
foo := &x509.Certificate{Subject: pkix.Name{CommonName: "foo"}}
bar := &x509.Certificate{Subject: pkix.Name{CommonName: "bar"}}
baz := &x509.Certificate{Subject: pkix.Name{CommonName: "baz"}}
return test{ return test{
o: o, o: o,
csr: csr, csr: csr,
@ -814,3 +989,201 @@ func Test_uniqueSortedIPs(t *testing.T) {
}) })
} }
} }
func Test_numberOfIdentifierType(t *testing.T) {
type args struct {
typ IdentifierType
ids []Identifier
}
tests := []struct {
name string
args args
want int
}{
{
name: "ok/no-identifiers",
args: args{
typ: DNS,
ids: []Identifier{},
},
want: 0,
},
{
name: "ok/no-dns",
args: args{
typ: DNS,
ids: []Identifier{
{
Type: IP,
Value: "192.168.42.42",
},
},
},
want: 0,
},
{
name: "ok/no-ips",
args: args{
typ: IP,
ids: []Identifier{
{
Type: DNS,
Value: "example.com",
},
},
},
want: 0,
},
{
name: "ok/one-dns",
args: args{
typ: DNS,
ids: []Identifier{
{
Type: DNS,
Value: "example.com",
},
{
Type: IP,
Value: "192.168.42.42",
},
},
},
want: 1,
},
{
name: "ok/one-ip",
args: args{
typ: IP,
ids: []Identifier{
{
Type: DNS,
Value: "example.com",
},
{
Type: IP,
Value: "192.168.42.42",
},
},
},
want: 1,
},
{
name: "ok/more-dns",
args: args{
typ: DNS,
ids: []Identifier{
{
Type: DNS,
Value: "example.com",
},
{
Type: DNS,
Value: "*.example.com",
},
{
Type: IP,
Value: "192.168.42.42",
},
},
},
want: 2,
},
{
name: "ok/more-ips",
args: args{
typ: IP,
ids: []Identifier{
{
Type: DNS,
Value: "example.com",
},
{
Type: IP,
Value: "192.168.42.42",
},
{
Type: IP,
Value: "192.168.42.43",
},
},
},
want: 2,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := numberOfIdentifierType(tt.args.typ, tt.args.ids); got != tt.want {
t.Errorf("numberOfIdentifierType() = %v, want %v", got, tt.want)
}
})
}
}
func Test_ipsAreEqual(t *testing.T) {
type args struct {
x net.IP
y net.IP
}
tests := []struct {
name string
args args
want bool
}{
{
name: "ok/ipv4",
args: args{
x: net.ParseIP("192.168.42.42"),
y: net.ParseIP("192.168.42.42"),
},
want: true,
},
{
name: "ok/ipv6",
args: args{
x: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
},
want: true,
},
{
name: "ok/ipv4-and-ipv6",
args: args{
x: net.ParseIP("192.168.42.42"),
y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
},
want: false,
},
{
name: "ok/ipv4-mapped-to-ipv6",
args: args{
x: net.ParseIP("192.168.42.42"),
y: net.ParseIP("::ffff:192.168.42.42"), // parsed to the same IPv4 by Go
},
want: true, // TODO: is this behavior OK?
},
{
name: "ok/invalid-ipv4-and-valid-ipv6",
args: args{
x: net.ParseIP("192.168.42.1000"),
y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
},
want: false,
},
{
name: "ok/invalid-ipv4-and-invalid-ipv6",
args: args{
x: net.ParseIP("192.168.42.1000"),
y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:1000000"),
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ipsAreEqual(tt.args.x, tt.args.y); got != tt.want {
t.Errorf("ipsAreEqual() = %v, want %v", got, tt.want)
}
})
}
}