forked from TrueCloudLab/certificates
2773 lines
64 KiB
Go
Executable file
2773 lines
64 KiB
Go
Executable file
package policy
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"net"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/smallstep/assert"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// TODO(hs): the functionality in the policy engine is a nice candidate for trying fuzzing on
|
|
// TODO(hs): more complex uses cases that combine multiple names and permitted/excluded entries
|
|
|
|
func TestNamePolicyEngine_matchDomainConstraint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
engine *NamePolicyEngine
|
|
domain string
|
|
constraint string
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "fail/wildcard",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "host.local",
|
|
constraint: ".example.com", // internally we're using the x509 period prefix as the indicator for exactly one subdomain
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/wildcard-literal",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "*.example.com",
|
|
constraint: ".example.com", // internally we're using the x509 period prefix as the indicator for exactly one subdomain
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/specific-domain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "www.example.com",
|
|
constraint: "host.example.com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/single-whitespace-domain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: " ",
|
|
constraint: "host.example.com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/period-domain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: ".host.example.com",
|
|
constraint: ".example.com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/wrong-asterisk-prefix",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "*Xexample.com",
|
|
constraint: ".example.com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/asterisk-in-domain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "e*ample.com",
|
|
constraint: ".com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/asterisk-label",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "example.*.local",
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/multiple-periods",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "example.local",
|
|
constraint: "..local",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/error-parsing-domain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: string([]byte{0}),
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/error-parsing-constraint",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "example.local",
|
|
constraint: string([]byte{0}),
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/no-subdomain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "local",
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/too-many-subdomains",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "www.example.local",
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/wrong-domain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "example.notlocal",
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "false/idna-internationalized-domain-name",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "JP納豆.例.jp", // Example value from https://www.w3.org/International/articles/idn-and-iri/
|
|
constraint: ".例.jp",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "false/idna-internationalized-domain-name-constraint",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "xn--jp-cd2fp15c.xn--fsq.jp", // Example value from https://www.w3.org/International/articles/idn-and-iri/
|
|
constraint: ".例.jp",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "ok/empty-constraint",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "www.example.com",
|
|
constraint: "",
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/wildcard",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "www.example.com",
|
|
constraint: ".example.com", // internally we're using the x509 period prefix as the indicator for exactly one subdomain
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/wildcard-literal",
|
|
engine: &NamePolicyEngine{
|
|
allowLiteralWildcardNames: true,
|
|
},
|
|
domain: "*.example.com", // specifically allowed using an option on the NamePolicyEngine
|
|
constraint: ".example.com", // internally we're using the x509 period prefix as the indicator for exactly one subdomain
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/specific-domain",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "www.example.com",
|
|
constraint: "www.example.com",
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/different-case",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "WWW.EXAMPLE.com",
|
|
constraint: "www.example.com",
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/idna-internationalized-domain-name-punycode",
|
|
engine: &NamePolicyEngine{},
|
|
domain: "xn--jp-cd2fp15c.xn--fsq.jp", // Example value from https://www.w3.org/International/articles/idn-and-iri/
|
|
constraint: ".xn--fsq.jp",
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := tt.engine.matchDomainConstraint(tt.domain, tt.constraint)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("NamePolicyEngine.matchDomainConstraint() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("NamePolicyEngine.matchDomainConstraint() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_matchIPConstraint(t *testing.T) {
|
|
nat64IP, nat64Net, err := net.ParseCIDR("64:ff9b::/96")
|
|
assert.FatalError(t, err)
|
|
tests := []struct {
|
|
name string
|
|
ip net.IP
|
|
constraint *net.IPNet
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "false/ipv4-in-ipv6-nat64",
|
|
ip: net.ParseIP("192.0.2.128"),
|
|
constraint: nat64Net,
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv4",
|
|
ip: net.ParseIP("127.0.0.1"),
|
|
constraint: &net.IPNet{
|
|
IP: net.ParseIP("127.0.0.0"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv6",
|
|
ip: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7335"),
|
|
constraint: &net.IPNet{
|
|
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
Mask: net.CIDRMask(120, 128),
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv4-in-ipv6", // ipv4 in ipv6 addresses are considered the same in the current implementation, because Go parses them as IPv4
|
|
ip: net.ParseIP("::ffff:192.0.2.128"),
|
|
constraint: &net.IPNet{
|
|
IP: net.ParseIP("192.0.2.0"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv4-in-ipv6-nat64-fixed-ip",
|
|
ip: nat64IP,
|
|
constraint: nat64Net,
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv4-in-ipv6-nat64",
|
|
ip: net.ParseIP("64:ff9b::192.0.2.129"),
|
|
constraint: nat64Net,
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := matchIPConstraint(tt.ip, tt.constraint)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("matchIPConstraint() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("matchIPConstraint() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNamePolicyEngine_matchEmailConstraint(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
engine *NamePolicyEngine
|
|
mailbox rfc2821Mailbox
|
|
constraint string
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "fail/asterisk-prefix",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "*@example.com",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/asterisk-label",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "@host.*.example.com",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/asterisk-inside-local",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "m*il@local",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/asterisk-inside-domain",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "@h*st.example.com",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/parse-email",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "@example.com",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/wildcard",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "example.com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/wildcard-x509-period",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: ".local", // "wildcard" for the local domain; requires exactly 1 subdomain
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/specific-mail-wrong-domain",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "mail@example.com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/specific-mail-wrong-local",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "root",
|
|
domain: "example.com",
|
|
},
|
|
constraint: "mail@example.com",
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/wildcard",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "local", // "wildcard" for the local domain
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/wildcard-x509-period",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "example.local",
|
|
},
|
|
constraint: ".local", // "wildcard" for the local domain; requires exactly 1 subdomain
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/specific-mail",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "local",
|
|
},
|
|
constraint: "mail@local",
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/wildcard-tld",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "example.com",
|
|
},
|
|
constraint: "example.com", // "wildcard" for 'example.com'
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/different-case",
|
|
engine: &NamePolicyEngine{},
|
|
mailbox: rfc2821Mailbox{
|
|
local: "mail",
|
|
domain: "EXAMPLE.com",
|
|
},
|
|
constraint: "example.com", // "wildcard" for 'example.com'
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := tt.engine.matchEmailConstraint(tt.mailbox, tt.constraint)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("NamePolicyEngine.matchEmailConstraint() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("NamePolicyEngine.matchEmailConstraint() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNamePolicyEngine_matchURIConstraint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
engine *NamePolicyEngine
|
|
uri *url.URL
|
|
constraint string
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "fail/empty-host",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "",
|
|
},
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-asterisk-prefix",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "*.local",
|
|
},
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-asterisk-label",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "host.*.local",
|
|
},
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-asterisk-inside",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "h*st.local",
|
|
},
|
|
constraint: ".local",
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/wildcard",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "www.example.notlocal",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/wildcard-subdomains-too-deep",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "www.sub.example.local",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "fail/host-with-port-split-error",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "www.example.local::8080",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-ipv4",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "127.0.0.1",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-ipv6",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "ok/wildcard",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "www.example.local",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/host-with-port",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "www.example.local:8080",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/different-case",
|
|
engine: &NamePolicyEngine{},
|
|
uri: &url.URL{
|
|
Scheme: "https",
|
|
Host: "www.EXAMPLE.local",
|
|
},
|
|
constraint: ".example.local", // using x509 period as the "wildcard"; expects a single subdomain
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := tt.engine.matchURIConstraint(tt.uri, tt.constraint)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("NamePolicyEngine.matchURIConstraint() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("NamePolicyEngine.matchURIConstraint() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func extractSANs(cert *x509.Certificate, includeSubject bool) []string {
|
|
sans := []string{}
|
|
sans = append(sans, cert.DNSNames...)
|
|
for _, ip := range cert.IPAddresses {
|
|
sans = append(sans, ip.String())
|
|
}
|
|
sans = append(sans, cert.EmailAddresses...)
|
|
for _, uri := range cert.URIs {
|
|
sans = append(sans, uri.String())
|
|
}
|
|
if includeSubject && cert.Subject.CommonName != "" {
|
|
sans = append(sans, cert.Subject.CommonName)
|
|
}
|
|
return sans
|
|
}
|
|
|
|
func TestNamePolicyEngine_X509_AllAllowed(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
options []NamePolicyOption
|
|
cert *x509.Certificate
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
// SINGLE SAN TYPE PERMITTED FAILURE TESTS
|
|
{
|
|
name: "fail/dns-permitted",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"www.example.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-wildcard-literal-x509",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.x509local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{
|
|
"*.x509local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-single-host",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("host.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"differenthost.local"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-no-label",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"local"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-empty-label",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"www..local"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-dot-domain",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{
|
|
".local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-wildcard-multiple-subdomains",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{
|
|
"sub.example.local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-wildcard-literal",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{
|
|
"*.local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/ipv4-permitted",
|
|
options: []NamePolicyOption{
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("1.1.1.1")},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/ipv6-permitted",
|
|
options: []NamePolicyOption{
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
Mask: net.CIDRMask(120, 128),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("3001:0db8:85a3:0000:0000:8a2e:0370:7334")},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-permitted-wildcard",
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{
|
|
"test@local.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-permitted-wildcard-x509",
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{
|
|
"test@local.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-permitted-specific-mailbox",
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("test@local.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{
|
|
"root@local.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-permitted-wildcard-subdomain",
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{
|
|
"test@sub.example.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/permitted-uri-domain-wildcard",
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/permitted-uri",
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("test.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "bad.local",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/permitted-uri-with-literal-wildcard", // don't allow literal wildcard in URI, e.g. xxxx://*.domain.tld
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "*.local",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
// SINGLE SAN TYPE EXCLUDED FAILURE TESTS
|
|
{
|
|
name: "fail/dns-excluded",
|
|
options: []NamePolicyOption{
|
|
AddExcludedDNSDomain("*.example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"www.example.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-excluded-single-host",
|
|
options: []NamePolicyOption{
|
|
AddExcludedDNSDomain("host.example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"host.example.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/ipv4-excluded",
|
|
options: []NamePolicyOption{
|
|
AddExcludedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/ipv6-excluded",
|
|
options: []NamePolicyOption{
|
|
AddExcludedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
Mask: net.CIDRMask(120, 128),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-excluded",
|
|
options: []NamePolicyOption{
|
|
AddExcludedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@example.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/uri-excluded",
|
|
options: []NamePolicyOption{
|
|
AddExcludedURIDomain("*.example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
// SUBJECT FAILURE TESTS
|
|
{
|
|
name: "fail/subject-dns-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "example.notlocal",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-dns-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "example.local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-ipv4-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "10.10.10.10",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-ipv4-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "127.0.0.30",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-ipv6-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
Mask: net.CIDRMask(120, 128),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "2002:0db8:85a3:0000:0000:8a2e:0370:7339",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-ipv6-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
Mask: net.CIDRMask(120, 128),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "2001:0db8:85a3:0000:0000:8a2e:0370:7339",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-email-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedEmailAddress("@example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "mail@smallstep.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-email-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedEmailAddress("@example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "mail@example.local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-uri-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedURIDomain("*.example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "https://www.google.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/subject-uri-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedURIDomain("*.example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "https://www.example.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
// DIFFERENT SAN PERMITTED FAILURE TESTS
|
|
{
|
|
name: "fail/dns-permitted-with-ip-name", // when only DNS is permitted, IPs are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-with-mail", // when only DNS is permitted, mails are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@smallstep.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/dns-permitted-with-uri", // when only DNS is permitted, URIs are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/ip-permitted-with-dns-name", // when only IP is permitted, DNS names are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"www.example.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/ip-permitted-with-mail", // when only IP is permitted, mails are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@smallstep.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/ip-permitted-with-uri", // when only IP is permitted, URIs are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-permitted-with-dns-name", // when only mail is permitted, DNS names are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"www.example.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-permitted-with-ip", // when only mail is permitted, IPs are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{
|
|
net.ParseIP("127.0.0.1"),
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/mail-permitted-with-uri", // when only mail is permitted, URIs are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/uri-permitted-with-dns-name", // when only URI is permitted, DNS names are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"host.local"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/uri-permitted-with-ip-name", // when only URI is permitted, IPs are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{
|
|
net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/uri-permitted-with-ip-name", // when only URI is permitted, mails are not allowed.
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@smallstep.com"},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
// COMBINED FAILURE TESTS
|
|
{
|
|
name: "fail/combined-simple-all-badhost.local",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
WithPermittedDNSDomain("*.local"),
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
WithPermittedEmailAddress("@example.local"),
|
|
WithPermittedURIDomain("*.example.local"),
|
|
WithExcludedDNSDomain("badhost.local"),
|
|
WithExcludedCIDR("127.0.0.128/25"),
|
|
WithExcludedEmailAddress("badmail@example.local"),
|
|
WithExcludedURIDomain("badwww.example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "badhost.local",
|
|
},
|
|
DNSNames: []string{"example.local"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.40")},
|
|
EmailAddresses: []string{"mail@example.local"},
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.local",
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
// NO CONSTRAINT SUCCESS TESTS
|
|
{
|
|
name: "ok/dns-no-constraints",
|
|
options: []NamePolicyOption{},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"www.example.com"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv4-no-constraints",
|
|
options: []NamePolicyOption{},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{
|
|
net.ParseIP("127.0.0.1"),
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv6-no-constraints",
|
|
options: []NamePolicyOption{},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{
|
|
net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-no-constraints",
|
|
options: []NamePolicyOption{},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@smallstep.com"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-no-constraints",
|
|
options: []NamePolicyOption{},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-no-constraints",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "www.example.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-empty-no-constraints",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
// SINGLE SAN TYPE PERMITTED SUCCESS TESTS
|
|
{
|
|
name: "ok/dns-permitted",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"example.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/dns-permitted-wildcard",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
AddPermittedDNSDomain(".x509local"),
|
|
WithAllowLiteralWildcardNames(),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{
|
|
"host.local",
|
|
"test.x509local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/empty-dns-constraint",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain(""),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"example.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/dns-permitted-wildcard-literal",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
AddPermittedDNSDomain("*.x509local"),
|
|
WithAllowLiteralWildcardNames(),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{
|
|
"*.local",
|
|
"*.x509local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/dns-permitted-combined",
|
|
options: []NamePolicyOption{
|
|
AddPermittedDNSDomain("*.local"),
|
|
AddPermittedDNSDomain("*.x509local"),
|
|
AddPermittedDNSDomain("host.example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{
|
|
"example.local",
|
|
"example.x509local",
|
|
"host.example.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv4-permitted",
|
|
options: []NamePolicyOption{
|
|
AddPermittedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.20")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv6-permitted",
|
|
options: []NamePolicyOption{
|
|
AddPermittedCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/120"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7339")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-permitted-wildcard",
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{
|
|
"test@example.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-permitted-plain-domain",
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{
|
|
"test@example.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-permitted-specific-mailbox",
|
|
options: []NamePolicyOption{
|
|
AddPermittedEmailAddress("test@local.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{
|
|
"test@local.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-permitted-domain-wildcard",
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "example.local",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-permitted-specific-uri",
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain("test.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "test.local",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-permitted-with-port",
|
|
options: []NamePolicyOption{
|
|
AddPermittedURIDomain(".example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com:8080",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
// SINGLE SAN TYPE EXCLUDED SUCCESS TESTS
|
|
{
|
|
name: "ok/dns-excluded",
|
|
options: []NamePolicyOption{
|
|
WithExcludedDNSDomain("*.notlocal"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"example.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv4-excluded",
|
|
options: []NamePolicyOption{
|
|
AddExcludedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("10.10.10.10")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ipv6-excluded",
|
|
options: []NamePolicyOption{
|
|
AddExcludedCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/120"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("2003:0db8:85a3:0000:0000:8a2e:0370:7334")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-excluded",
|
|
options: []NamePolicyOption{
|
|
WithExcludedEmailAddress("@notlocal"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-excluded-with-subdomain",
|
|
options: []NamePolicyOption{
|
|
WithExcludedEmailAddress("@local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@example.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-excluded",
|
|
options: []NamePolicyOption{
|
|
WithExcludedURIDomain("*.google.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
// SUBJECT SUCCESS TESTS
|
|
{
|
|
name: "ok/subject-empty",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "",
|
|
},
|
|
DNSNames: []string{"example.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-dns-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "example.local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-dns-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedDNSDomain("*.notlocal"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "example.local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-ipv4-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "127.0.0.20",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-ipv4-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("128.0.0.1"),
|
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "127.0.0.1",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-ipv6-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
Mask: net.CIDRMask(120, 128),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "2001:0db8:85a3:0000:0000:8a2e:0370:7339",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-ipv6-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedIPRanges(
|
|
[]*net.IPNet{
|
|
{
|
|
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
|
|
Mask: net.CIDRMask(120, 128),
|
|
},
|
|
},
|
|
),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "2009:0db8:85a3:0000:0000:8a2e:0370:7339",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-email-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedEmailAddress("@example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "mail@example.local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-email-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedEmailAddress("@example.notlocal"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "mail@example.local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-uri-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddPermittedURIDomain("*.example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "https://www.example.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/subject-uri-excluded",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedURIDomain("*.smallstep.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "https://www.example.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
// DIFFERENT SAN TYPE EXCLUDED SUCCESS TESTS
|
|
{
|
|
name: "ok/dns-excluded-with-ip-name", // when only DNS is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
AddExcludedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/dns-excluded-with-mail", // when only DNS is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
AddExcludedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@example.com"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/dns-excluded-with-mail", // when only DNS is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
AddExcludedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ip-excluded-with-dns", // when only IP is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"test.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ip-excluded-with-mail", // when only IP is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@example.com"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/ip-excluded-with-mail", // when only IP is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-excluded-with-dns", // when only mail is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"test.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-excluded-with-ip", // when only mail is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/mail-excluded-with-uri", // when only mail is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedEmailAddress("@example.com"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.com",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-excluded-with-dns", // when only URI is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedURIDomain("*.example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
DNSNames: []string{"test.example.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-excluded-with-dns", // when only URI is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedURIDomain("*.example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/uri-excluded-with-mail", // when only URI is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithExcludedURIDomain("*.example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
EmailAddresses: []string{"mail@example.local"},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/dns-excluded-with-subject-ip-name", // when only DNS is exluded, we allow anything else
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
AddExcludedDNSDomain("*.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "127.0.0.1",
|
|
},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
// COMBINED SUCCESS TESTS
|
|
{
|
|
name: "ok/combined-simple-permitted",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
WithPermittedDNSDomain("*.local"),
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
WithPermittedEmailAddress("@example.local"),
|
|
WithPermittedURIDomain("*.example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "somehost.local",
|
|
},
|
|
DNSNames: []string{"example.local"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.15")},
|
|
EmailAddresses: []string{"mail@example.local"},
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.local",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/combined-simple-permitted-without-subject-verification",
|
|
options: []NamePolicyOption{
|
|
WithPermittedDNSDomain("*.local"),
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
WithPermittedEmailAddress("@example.local"),
|
|
WithPermittedURIDomain("*.example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "forbidden-but-non-verified-domain.example.com",
|
|
},
|
|
DNSNames: []string{"example.local"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
EmailAddresses: []string{"mail@example.local"},
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.local",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/combined-simple-all",
|
|
options: []NamePolicyOption{
|
|
WithSubjectCommonNameVerification(),
|
|
WithPermittedDNSDomain("*.local"),
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
WithPermittedEmailAddress("@example.local"),
|
|
WithPermittedURIDomain("*.example.local"),
|
|
WithExcludedDNSDomain("badhost.local"),
|
|
WithExcludedCIDR("127.0.0.128/25"),
|
|
WithExcludedEmailAddress("badmail@example.local"),
|
|
WithExcludedURIDomain("badwww.example.local"),
|
|
},
|
|
cert: &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "somehost.local",
|
|
},
|
|
DNSNames: []string{"example.local"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
EmailAddresses: []string{"mail@example.local"},
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "www.example.local",
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
engine, err := New(tt.options...)
|
|
assert.FatalError(t, err)
|
|
got, err := engine.AreCertificateNamesAllowed(tt.cert)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("NamePolicyEngine.AreCertificateNamesAllowed() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if err != nil {
|
|
assert.NotEquals(t, "", err.Error()) // TODO(hs): implement a more specific error comparison?
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("NamePolicyEngine.AreCertificateNamesAllowed() = %v, want %v", got, tt.want)
|
|
}
|
|
|
|
// Perform the same tests for a CSR, which are similar to Certificates
|
|
csr := &x509.CertificateRequest{
|
|
Subject: tt.cert.Subject,
|
|
DNSNames: tt.cert.DNSNames,
|
|
EmailAddresses: tt.cert.EmailAddresses,
|
|
IPAddresses: tt.cert.IPAddresses,
|
|
URIs: tt.cert.URIs,
|
|
}
|
|
got, err = engine.AreCSRNamesAllowed(csr)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("NamePolicyEngine.AreCSRNamesAllowed() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if err != nil {
|
|
assert.NotEquals(t, "", err.Error())
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("NamePolicyEngine.AreCSRNamesAllowed() = %v, want %v", got, tt.want)
|
|
}
|
|
|
|
// Perform the same tests for a slice of SANs
|
|
includeSubject := engine.verifySubjectCommonName // copy behavior of the engine when Subject has to be included as a SAN
|
|
sans := extractSANs(tt.cert, includeSubject)
|
|
got, err = engine.AreSANsAllowed(sans)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("NamePolicyEngine.AreSANsAllowed() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if err != nil {
|
|
assert.NotEquals(t, "", err.Error())
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("NamePolicyEngine.AreSANsAllowed() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNamePolicyEngine_SSH_ArePrincipalsAllowed(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
options []NamePolicyOption
|
|
cert *ssh.Certificate
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "fail/host-with-permitted-dns-domain",
|
|
options: []NamePolicyOption{
|
|
WithPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"host.example.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-excluded-dns-domain",
|
|
options: []NamePolicyOption{
|
|
WithExcludedDNSDomain("*.local"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"host.local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-permitted-ip",
|
|
options: []NamePolicyOption{
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"192.168.0.22",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-excluded-ip",
|
|
options: []NamePolicyOption{
|
|
WithExcludedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"127.0.0.0",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/user-with-permitted-email",
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"mail@local",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/user-with-excluded-email",
|
|
options: []NamePolicyOption{
|
|
WithExcludedEmailAddress("@example.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"mail@example.com",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-permitted-principals",
|
|
options: []NamePolicyOption{
|
|
WithPermittedPrincipals([]string{"localhost"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"host",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-with-excluded-principals",
|
|
options: []NamePolicyOption{
|
|
WithExcludedPrincipals([]string{"localhost"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
ValidPrincipals: []string{
|
|
"localhost",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/user-with-permitted-principals",
|
|
options: []NamePolicyOption{
|
|
WithPermittedPrincipals([]string{"user"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"root",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/user-with-excluded-principals",
|
|
options: []NamePolicyOption{
|
|
WithExcludedPrincipals([]string{"user"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"user",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/user-with-permitted-principal-as-mail",
|
|
options: []NamePolicyOption{
|
|
WithPermittedPrincipals([]string{"ops"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"ops@work", // this is (currently) parsed as an email-like principal; not allowed with just "ops" as the permitted principal
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-principal-with-permitted-dns-domain", // when only DNS is permitted, username principals are not allowed.
|
|
options: []NamePolicyOption{
|
|
WithPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"user",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/host-principal-with-permitted-ip-range", // when only IPs are permitted, username principals are not allowed.
|
|
options: []NamePolicyOption{
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"user",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/user-principal-with-permitted-email", // when only emails are permitted, username principals are not allowed.
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"user",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/combined-user",
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@smallstep.com"),
|
|
WithExcludedEmailAddress("root@smallstep.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"someone@smallstep.com",
|
|
"someone",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fail/combined-user-with-excluded-user-principal",
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@smallstep.com"),
|
|
WithExcludedPrincipals([]string{"root"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"someone@smallstep.com",
|
|
"root",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "ok/host-with-permitted-user-principals",
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@work"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"example.work",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "ok/user-with-permitted-user-principals",
|
|
options: []NamePolicyOption{
|
|
WithPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"herman@work",
|
|
},
|
|
},
|
|
want: false,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "ok/host-with-permitted-dns-domain",
|
|
options: []NamePolicyOption{
|
|
WithPermittedDNSDomain("*.local"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"host.local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/host-with-excluded-dns-domain",
|
|
options: []NamePolicyOption{
|
|
WithExcludedDNSDomain("*.example.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"host.local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/host-with-permitted-ip",
|
|
options: []NamePolicyOption{
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"127.0.0.33",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/host-with-excluded-ip",
|
|
options: []NamePolicyOption{
|
|
WithExcludedCIDR("127.0.0.1/24"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"192.168.0.35",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/user-with-permitted-email",
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@example.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"mail@example.com",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/user-with-excluded-email",
|
|
options: []NamePolicyOption{
|
|
WithExcludedEmailAddress("@example.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"mail@local",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/user-with-permitted-principals",
|
|
options: []NamePolicyOption{
|
|
WithPermittedPrincipals([]string{"*"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"user",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/user-with-excluded-principals",
|
|
options: []NamePolicyOption{
|
|
WithExcludedPrincipals([]string{"user"}),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"root",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/combined-user",
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@smallstep.com"),
|
|
WithPermittedPrincipals([]string{"*"}), // without specifying the wildcard, "someone" would not be allowed.
|
|
WithExcludedEmailAddress("root@smallstep.com"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"someone@smallstep.com",
|
|
"someone",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/combined-user-with-excluded-user-principal",
|
|
options: []NamePolicyOption{
|
|
WithPermittedEmailAddress("@smallstep.com"),
|
|
WithExcludedEmailAddress("root@smallstep.com"),
|
|
WithExcludedPrincipals([]string{"root"}), // unlike the previous test, this implicitly allows any other username principal
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{
|
|
"someone@smallstep.com",
|
|
"someone",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ok/combined-host",
|
|
options: []NamePolicyOption{
|
|
WithPermittedDNSDomain("*.local"),
|
|
WithPermittedCIDR("127.0.0.1/24"),
|
|
WithExcludedDNSDomain("badhost.local"),
|
|
WithExcludedCIDR("127.0.0.128/25"),
|
|
},
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{
|
|
"example.local",
|
|
"127.0.0.31",
|
|
},
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
engine, err := New(tt.options...)
|
|
assert.FatalError(t, err)
|
|
got, err := engine.ArePrincipalsAllowed(tt.cert)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("NamePolicyEngine.ArePrincipalsAllowed() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("NamePolicyEngine.ArePrincipalsAllowed() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type result struct {
|
|
wantDNSNames []string
|
|
wantIps []net.IP
|
|
wantEmails []string
|
|
wantUsernames []string
|
|
}
|
|
|
|
func emptyResult() result {
|
|
return result{
|
|
wantDNSNames: []string{},
|
|
wantIps: []net.IP{},
|
|
wantEmails: []string{},
|
|
wantUsernames: []string{},
|
|
}
|
|
}
|
|
|
|
func Test_splitSSHPrincipals(t *testing.T) {
|
|
type test struct {
|
|
cert *ssh.Certificate
|
|
r result
|
|
wantErr bool
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/unexpected-cert-type": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: uint32(0),
|
|
},
|
|
r: r,
|
|
wantErr: true,
|
|
}
|
|
},
|
|
"fail/user-ip": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
r.wantIps = []net.IP{net.ParseIP("127.0.0.1")} // this will still be in the result
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{"127.0.0.1"},
|
|
},
|
|
r: r,
|
|
wantErr: true,
|
|
}
|
|
},
|
|
"fail/user-uri": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{"https://host.local/"},
|
|
},
|
|
r: r,
|
|
wantErr: true,
|
|
}
|
|
},
|
|
"fail/host-email": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
r.wantEmails = []string{"ops@work"} // this will still be in the result
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{"ops@work"},
|
|
},
|
|
r: r,
|
|
wantErr: true,
|
|
}
|
|
},
|
|
"fail/host-uri": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{"https://host.local/"},
|
|
},
|
|
r: r,
|
|
wantErr: true,
|
|
}
|
|
},
|
|
"ok/host-dns": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
r.wantDNSNames = []string{"host.example.com"}
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{"host.example.com"},
|
|
},
|
|
r: r,
|
|
}
|
|
},
|
|
"ok/host-ip": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
r.wantIps = []net.IP{net.ParseIP("127.0.0.1")}
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.HostCert,
|
|
ValidPrincipals: []string{"127.0.0.1"},
|
|
},
|
|
r: r,
|
|
}
|
|
},
|
|
"ok/user-localhost": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
r.wantUsernames = []string{"localhost"} // when type is User cert, this is considered a username; not a DNS
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{"localhost"},
|
|
},
|
|
r: r,
|
|
}
|
|
},
|
|
"ok/user-username-with-period": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
r.wantUsernames = []string{"x.joe"}
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{"x.joe"},
|
|
},
|
|
r: r,
|
|
}
|
|
},
|
|
"ok/user-maillike": func(t *testing.T) test {
|
|
r := emptyResult()
|
|
r.wantEmails = []string{"ops@work"}
|
|
return test{
|
|
cert: &ssh.Certificate{
|
|
CertType: ssh.UserCert,
|
|
ValidPrincipals: []string{"ops@work"},
|
|
},
|
|
r: r,
|
|
}
|
|
},
|
|
}
|
|
for name, prep := range tests {
|
|
tt := prep(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
gotDNSNames, gotIps, gotEmails, gotUsernames, err := splitSSHPrincipals(tt.cert)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("splitSSHPrincipals() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !cmp.Equal(tt.r.wantDNSNames, gotDNSNames) {
|
|
t.Errorf("splitSSHPrincipals() DNS names diff =\n%s", cmp.Diff(tt.r.wantDNSNames, gotDNSNames))
|
|
}
|
|
if !cmp.Equal(tt.r.wantIps, gotIps) {
|
|
t.Errorf("splitSSHPrincipals() IPs diff =\n%s", cmp.Diff(tt.r.wantIps, gotIps))
|
|
}
|
|
if !cmp.Equal(tt.r.wantEmails, gotEmails) {
|
|
t.Errorf("splitSSHPrincipals() Emails diff =\n%s", cmp.Diff(tt.r.wantEmails, gotEmails))
|
|
}
|
|
if !cmp.Equal(tt.r.wantUsernames, gotUsernames) {
|
|
t.Errorf("splitSSHPrincipals() Usernames diff =\n%s", cmp.Diff(tt.r.wantUsernames, gotUsernames))
|
|
}
|
|
})
|
|
}
|
|
}
|