Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexport Connect, add CFFSL test for unencrypted connections #28

Merged
merged 4 commits into from
Jan 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions issuers/aws/aws_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
. "github.com/onsi/gomega"
)

func TestAws(t *testing.T) {
func TestAWS(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Aws Suite")
RunSpecs(t, "AWS Suite")
}
17 changes: 10 additions & 7 deletions issuers/cfssl/cfssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ func FromClient(v client.Remote) (*Issuer, error) {
return i, nil
}

// Connect creates a new connection to the CFSSL server
// and sends a request to validate server availability. If not called,
// a connection will be made in the first Issue call.
func (i *Issuer) Connect(ctx context.Context) error {
i.remote = client.NewServerTLS(i.URL.String(), i.TLSConfig)
// connect and sends a request to validate server availability and
// cache its cert.
func (i *Issuer) connect(ctx context.Context) error {
if i.TLSConfig != nil {
i.remote = client.NewServerTLS(i.URL.String(), i.TLSConfig)
} else {
i.remote = client.NewServer(i.URL.String())
}
// Add context to requests
i.remote.SetReqModifier(func(req *http.Request, _ []byte) {
*req = *req.WithContext(ctx)
Expand All @@ -84,10 +87,10 @@ func (i *Issuer) Connect(ctx context.Context) error {
return nil
}

// Issue issues a certificate with the provided options
// Issue issues a certificate with the provided options.
func (i *Issuer) Issue(ctx context.Context, commonName string, conf *certify.CertConfig) (*tls.Certificate, error) {
if i.remote == nil {
err := i.Connect(ctx)
err := i.connect(ctx)
if err != nil {
return nil, err
}
Expand Down
155 changes: 143 additions & 12 deletions issuers/cfssl/cfssl_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ type cfsslConfig struct {
}

var (
pool *dockertest.Pool
resource *dockertest.Resource
waiter docker.CloseWaiter
pool *dockertest.Pool
resources []*dockertest.Resource
waiters []docker.CloseWaiter

cfsslConf cfsslConfig
cfsslConf, cfsslTLSConf cfsslConfig
)

var _ = BeforeSuite(func() {
Expand Down Expand Up @@ -99,8 +99,6 @@ var _ = BeforeSuite(func() {
"-port", "8888",
"-ca", "/cert.pem",
"-ca-key", "/key.pem",
"-tls-cert", "/cert.pem",
"-tls-key", "/key.pem",
"-config", "/conf.json",
},
},
Expand Down Expand Up @@ -168,7 +166,7 @@ var _ = BeforeSuite(func() {
c, err = pool.Client.InspectContainer(c.ID)
Expect(err).To(Succeed())

waiter, err = pool.Client.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
waiter, err := pool.Client.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
Container: c.ID,
OutputStream: GinkgoWriter,
ErrorStream: GinkgoWriter,
Expand All @@ -177,11 +175,12 @@ var _ = BeforeSuite(func() {
Stream: true,
})
Expect(err).To(Succeed())
waiters = append(waiters, waiter)

resource = &dockertest.Resource{Container: c}
resources = append(resources, &dockertest.Resource{Container: c})

cfsslConf.URL = &url.URL{
Scheme: "https",
Scheme: "http",
Host: net.JoinHostPort(host, "8888"),
}
cfsslConf.AuthKey = conf.AuthKeys[authKey].Key
Expand All @@ -192,12 +191,144 @@ var _ = BeforeSuite(func() {
return err
})).To(Succeed())
})

By("Starting the CFSSL TLS container", func() {
cp := x509.NewCertPool()
Expect(cp.AppendCertsFromPEM(cert)).To(BeTrue())
cfsslTLSConf = cfsslConfig{
Profile: "authed",
CertPool: cp,
}
repo := "cfssl/cfssl"
version := "1.3.2"
img := repo + ":" + version
_, err = pool.Client.InspectImage(img)
if err != nil {
// Pull image
Expect(pool.Client.PullImage(docker.PullImageOptions{
Repository: repo,
Tag: version,
OutputStream: GinkgoWriter,
}, docker.AuthConfiguration{})).To(Succeed())
}

c, err := pool.Client.CreateContainer(docker.CreateContainerOptions{
Name: "cfssl-tls",
Config: &docker.Config{
Image: img,
ExposedPorts: map[docker.Port]struct{}{
docker.Port("8888"): struct{}{},
},
Cmd: []string{
"serve",
"-loglevel", "0",
"-address", "0.0.0.0",
"-port", "8889",
"-ca", "/cert.pem",
"-ca-key", "/key.pem",
"-tls-cert", "/cert.pem",
"-tls-key", "/key.pem",
"-config", "/conf.json",
},
},
HostConfig: &docker.HostConfig{
NetworkMode: "host",
PublishAllPorts: true,
PortBindings: map[docker.Port][]docker.PortBinding{
"8889": []docker.PortBinding{{HostPort: "8889"}},
},
},
})
Expect(err).To(Succeed())

b := &bytes.Buffer{}
archive := tar.NewWriter(b)
Expect(archive.WriteHeader(&tar.Header{
Name: "/cert.pem",
Mode: 0644,
Size: int64(len(cert)),
})).To(Succeed())
Expect(archive.Write(cert)).To(Equal(len(cert)))
Expect(archive.WriteHeader(&tar.Header{
Name: "/key.pem",
Mode: 0644,
Size: int64(len(key)),
})).To(Succeed())
Expect(archive.Write(key)).To(Equal(len(key)))
const authKey = "testKey"
conf := config.Config{
Signing: &config.Signing{
Profiles: map[string]*config.SigningProfile{
cfsslTLSConf.Profile: &config.SigningProfile{
AuthKeyName: authKey,
Usage: []string{"signing", "key encipherment", "server auth", "client auth"},
Expiry: time.Hour * 8760,
ExpiryString: "8760h",
},
},
Default: config.DefaultConfig(),
},
AuthKeys: map[string]config.AuthKey{
authKey: config.AuthKey{
Type: "standard",
Key: "0123456789ABCDEF0123456789ABCDEF",
},
},
}
confBytes, err := json.Marshal(&conf)
Expect(err).To(Succeed())
Expect(archive.WriteHeader(&tar.Header{
Name: "/conf.json",
Mode: 0644,
Size: int64(len(confBytes)),
})).To(Succeed())
Expect(archive.Write(confBytes)).To(Equal(len(confBytes)))
Expect(archive.Close()).To(Succeed())

Expect(pool.Client.UploadToContainer(c.ID, docker.UploadToContainerOptions{
InputStream: b,
Path: "/",
})).To(Succeed())

Expect(pool.Client.StartContainer(c.ID, nil)).To(Succeed())

c, err = pool.Client.InspectContainer(c.ID)
Expect(err).To(Succeed())

waiter, err := pool.Client.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
Container: c.ID,
OutputStream: GinkgoWriter,
ErrorStream: GinkgoWriter,
Stderr: true,
Stdout: true,
Stream: true,
})
waiters = append(waiters, waiter)

resources = append(resources, &dockertest.Resource{Container: c})

cfsslTLSConf.URL = &url.URL{
Scheme: "https",
Host: net.JoinHostPort(host, "8889"),
}
cfsslTLSConf.AuthKey = conf.AuthKeys[authKey].Key

remote := client.NewServerTLS(cfsslTLSConf.URL.String(), &tls.Config{RootCAs: cp})
Expect(pool.Retry(func() error {
_, err = remote.Info([]byte(`{}`))
return err
})).To(Succeed())
})
})

var _ = AfterSuite(func() {
Expect(waiter.Close()).To(Succeed())
Expect(waiter.Wait()).To(Succeed())
Expect(pool.Purge(resource)).To(Succeed())
for _, waiter := range waiters {
Expect(waiter.Close()).To(Succeed())
Expect(waiter.Wait()).To(Succeed())
}
for _, resource := range resources {
Expect(pool.Purge(resource)).To(Succeed())
}
})

func generateCertAndKey(SAN string, IPSAN net.IP) ([]byte, []byte, error) {
Expand Down
122 changes: 115 additions & 7 deletions issuers/cfssl/cfssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,118 @@ var _ = Describe("CFSSL Issuer", func() {
BeforeEach(func() {
iss = &cfssl.Issuer{
URL: cfsslConf.URL,
}
})

It("issues a certificate", func() {
cn := "somename.com"

tlsCert, err := iss.Issue(context.Background(), cn, nil)
Expect(err).NotTo(HaveOccurred())

Expect(tlsCert.Leaf).NotTo(BeNil(), "tlsCert.Leaf should be populated by Issue to track expiry")
Expect(tlsCert.Leaf.Subject.CommonName).To(Equal(cn))

// Check that chain is included
Expect(tlsCert.Certificate).To(HaveLen(2))
caCert, err := x509.ParseCertificate(tlsCert.Certificate[1])
Expect(err).NotTo(HaveOccurred())
Expect(caCert.Subject.SerialNumber).To(Equal(tlsCert.Leaf.Issuer.SerialNumber))
})

Context("when specifying some SANs, IPSANs", func() {
It("issues a certificate with the SANs and IPSANs", func() {
conf := &certify.CertConfig{
SubjectAlternativeNames: []string{"extraname.com", "otherextraname.com"},
IPSubjectAlternativeNames: []net.IP{net.IPv4(1, 2, 3, 4), net.IPv6loopback},
}
cn := "somename.com"

tlsCert, err := iss.Issue(context.Background(), cn, conf)
Expect(err).NotTo(HaveOccurred())

Expect(tlsCert.Leaf).NotTo(BeNil(), "tlsCert.Leaf should be populated by Issue to track expiry")
Expect(tlsCert.Leaf.Subject.CommonName).To(Equal(cn))
Expect(tlsCert.Leaf.DNSNames).To(Equal(conf.SubjectAlternativeNames))
Expect(tlsCert.Leaf.IPAddresses).To(HaveLen(len(conf.IPSubjectAlternativeNames)))
for i, ip := range tlsCert.Leaf.IPAddresses {
Expect(ip.Equal(conf.IPSubjectAlternativeNames[i])).To(BeTrue())
}

// Check that chain is included
Expect(tlsCert.Certificate).To(HaveLen(2))
caCert, err := x509.ParseCertificate(tlsCert.Certificate[1])
Expect(err).NotTo(HaveOccurred())
Expect(caCert.Subject.SerialNumber).To(Equal(tlsCert.Leaf.Issuer.SerialNumber))
})
})
})

var _ = Describe("Authenticated CFSSL Issuer", func() {
var iss certify.Issuer

BeforeEach(func() {
st, err := auth.New(cfsslConf.AuthKey, nil)
Expect(err).To(Succeed())
iss = &cfssl.Issuer{
URL: cfsslConf.URL,
Auth: st,
Profile: cfsslConf.Profile,
}
})

It("issues a certificate", func() {
cn := "somename.com"

tlsCert, err := iss.Issue(context.Background(), cn, nil)
Expect(err).NotTo(HaveOccurred())

Expect(tlsCert.Leaf).NotTo(BeNil(), "tlsCert.Leaf should be populated by Issue to track expiry")
Expect(tlsCert.Leaf.Subject.CommonName).To(Equal(cn))

// Check that chain is included
Expect(tlsCert.Certificate).To(HaveLen(2))
caCert, err := x509.ParseCertificate(tlsCert.Certificate[1])
Expect(err).NotTo(HaveOccurred())
Expect(caCert.Subject.SerialNumber).To(Equal(tlsCert.Leaf.Issuer.SerialNumber))
})

Context("when specifying some SANs, IPSANs", func() {
It("issues a certificate with the SANs and IPSANs", func() {
conf := &certify.CertConfig{
SubjectAlternativeNames: []string{"extraname.com", "otherextraname.com"},
IPSubjectAlternativeNames: []net.IP{net.IPv4(1, 2, 3, 4), net.IPv6loopback},
}
cn := "somename.com"

tlsCert, err := iss.Issue(context.Background(), cn, conf)
Expect(err).NotTo(HaveOccurred())

Expect(tlsCert.Leaf).NotTo(BeNil(), "tlsCert.Leaf should be populated by Issue to track expiry")
Expect(tlsCert.Leaf.Subject.CommonName).To(Equal(cn))
Expect(tlsCert.Leaf.DNSNames).To(Equal(conf.SubjectAlternativeNames))
Expect(tlsCert.Leaf.IPAddresses).To(HaveLen(len(conf.IPSubjectAlternativeNames)))
for i, ip := range tlsCert.Leaf.IPAddresses {
Expect(ip.Equal(conf.IPSubjectAlternativeNames[i])).To(BeTrue())
}

// Check that chain is included
Expect(tlsCert.Certificate).To(HaveLen(2))
caCert, err := x509.ParseCertificate(tlsCert.Certificate[1])
Expect(err).NotTo(HaveOccurred())
Expect(caCert.Subject.SerialNumber).To(Equal(tlsCert.Leaf.Issuer.SerialNumber))
})
})
})

var _ = Describe("CFSSL TLS Issuer", func() {
var iss certify.Issuer

BeforeEach(func() {
iss = &cfssl.Issuer{
URL: cfsslTLSConf.URL,
TLSConfig: &tls.Config{
RootCAs: cfsslConf.CertPool,
RootCAs: cfsslTLSConf.CertPool,
},
}
})
Expand Down Expand Up @@ -78,12 +188,12 @@ var _ = Describe("Authenticated CFSSL Issuer", func() {
st, err := auth.New(cfsslConf.AuthKey, nil)
Expect(err).To(Succeed())
iss = &cfssl.Issuer{
URL: cfsslConf.URL,
URL: cfsslTLSConf.URL,
TLSConfig: &tls.Config{
RootCAs: cfsslConf.CertPool,
RootCAs: cfsslTLSConf.CertPool,
},
Auth: st,
Profile: cfsslConf.Profile,
Profile: cfsslTLSConf.Profile,
}
})

Expand Down Expand Up @@ -133,9 +243,7 @@ var _ = Describe("Authenticated CFSSL Issuer", func() {

var _ = Describe("Using a pre-created client", func() {
It("issues a certificate", func() {
remote := client.NewServerTLS(cfsslConf.URL.String(), &tls.Config{
RootCAs: cfsslConf.CertPool,
})
remote := client.NewServer(cfsslConf.URL.String())
iss, err := cfssl.FromClient(remote)
Expect(err).To(Succeed())

Expand Down
Loading