forked from juju/juju
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cert_test.go
171 lines (149 loc) · 5.62 KB
/
cert_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package apiserver_test
import (
"crypto/tls"
"runtime"
"time"
"github.com/juju/loggo"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/juju/api"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/cert"
coretesting "github.com/juju/juju/testing"
)
type certSuite struct {
apiserverBaseSuite
}
var _ = gc.Suite(&certSuite{})
func (s *certSuite) TestUpdateCert(c *gc.C) {
config := s.sampleConfig(c)
certChanged := make(chan params.StateServingInfo)
config.CertChanged = certChanged
srv := s.newServer(c, config)
// Sanity check that the server works initially.
conn := s.OpenAPIAsAdmin(c, srv)
c.Assert(pingConn(conn), jc.ErrorIsNil)
// Create a new certificate that's a year out of date, so we can
// tell that the server is using it because the connection
// will fail.
srvCert, srvKey, err := cert.NewServer(coretesting.CACert, coretesting.CAKey, time.Now().AddDate(-1, 0, 0), nil)
c.Assert(err, jc.ErrorIsNil)
info := params.StateServingInfo{
Cert: string(srvCert),
PrivateKey: string(srvKey),
// No other fields are used by the cert listener.
}
certChanged <- info
// Send the same info again so that we are sure that
// the previously received information was acted upon
// (an alternative would be to sleep for a while, but this
// approach is quicker and more certain).
certChanged <- info
// Check that we can't connect to the server because of the bad certificate.
apiInfo := s.APIInfo(srv)
apiInfo.Tag = s.Owner
apiInfo.Password = ownerPassword
_, err = api.Open(apiInfo, api.DialOpts{})
c.Assert(err, gc.ErrorMatches, `unable to connect to API: .*: certificate has expired or is not yet valid`)
// Now change it back and check that we can connect again.
info = params.StateServingInfo{
Cert: coretesting.ServerCert,
PrivateKey: coretesting.ServerKey,
// No other fields are used by the cert listener.
}
certChanged <- info
certChanged <- info
conn = s.OpenAPIAsAdmin(c, srv)
c.Assert(pingConn(conn), jc.ErrorIsNil)
}
func (s *certSuite) TestAutocertFailure(c *gc.C) {
// We don't have a fake autocert server, but we can at least
// smoke test that the autocert path is followed when we try
// to connect to a DNS name - the AutocertURL configured
// by the testing suite is invalid so it should fail.
config := s.sampleConfig(c)
config.AutocertDNSName = "somewhere.example"
srv := s.newServer(c, config)
apiInfo := s.APIInfo(srv)
entries := gatherLog(func() {
_, err := tls.Dial("tcp", apiInfo.Addrs[0], &tls.Config{
ServerName: "somewhere.example",
})
expectedErr := `x509: certificate is valid for \*, not somewhere.example`
if runtime.GOOS == "windows" {
// For some reason, windows doesn't think that the certificate is signed
// by a valid authority. This could be problematic.
expectedErr = "x509: certificate signed by unknown authority"
}
// We can't get an autocert certificate, so we'll fall back to the local certificate
// which isn't valid for connecting to somewhere.example.
c.Assert(err, gc.ErrorMatches, expectedErr)
})
// We will log the failure to get the certificate, thus assuring us that we actually tried.
c.Assert(entries, jc.LogMatches, jc.SimpleMessages{{
loggo.ERROR,
`.*cannot get autocert certificate for "somewhere.example": Get https://0\.1\.2\.3/no-autocert-here: .*`,
}})
}
func (s *certSuite) TestAutocertNameMismatch(c *gc.C) {
config := s.sampleConfig(c)
config.AutocertDNSName = "somewhere.example"
srv := s.newServer(c, config)
apiInfo := s.APIInfo(srv)
entries := gatherLog(func() {
_, err := tls.Dial("tcp", apiInfo.Addrs[0], &tls.Config{
ServerName: "somewhere.else",
})
expectedErr := `x509: certificate is valid for \*, not somewhere.else`
if runtime.GOOS == "windows" {
// For some reason, windows doesn't think that the certificate is signed
// by a valid authority. This could be problematic.
expectedErr = "x509: certificate signed by unknown authority"
}
// We can't get an autocert certificate, so we'll fall back to the local certificate
// which isn't valid for connecting to somewhere.example.
c.Assert(err, gc.ErrorMatches, expectedErr)
})
// Check that we logged the mismatch.
c.Assert(entries, jc.LogMatches, jc.SimpleMessages{{
loggo.ERROR,
`.*cannot get autocert certificate for "somewhere.else": acme/autocert: host not configured`,
}})
}
func (s *certSuite) TestAutocertNoAutocertDNSName(c *gc.C) {
config := s.sampleConfig(c)
c.Assert(config.AutocertDNSName, gc.Equals, "") // sanity check
srv := s.newServer(c, config)
apiInfo := s.APIInfo(srv)
entries := gatherLog(func() {
_, err := tls.Dial("tcp", apiInfo.Addrs[0], &tls.Config{
ServerName: "somewhere.example",
})
expectedErr := `x509: certificate is valid for \*, not somewhere.example`
if runtime.GOOS == "windows" {
// For some reason, windows doesn't think that the certificate is signed
// by a valid authority. This could be problematic.
expectedErr = "x509: certificate signed by unknown authority"
}
// We can't get an autocert certificate, so we'll fall back to the local certificate
// which isn't valid for connecting to somewhere.example.
c.Assert(err, gc.ErrorMatches, expectedErr)
})
// Check that we never logged a failure to get the certificate.
c.Assert(entries, gc.Not(jc.LogMatches), jc.SimpleMessages{{
loggo.ERROR,
`.*cannot get autocert certificate.*`,
}})
}
func gatherLog(f func()) []loggo.Entry {
var tw loggo.TestWriter
err := loggo.RegisterWriter("test", &tw)
if err != nil {
panic(err)
}
defer loggo.RemoveWriter("test")
f()
return tw.Log()
}