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