github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/httpserver/cert_test.go (about) 1 // Copyright 2016-2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package httpserver_test 5 6 import ( 7 "crypto/tls" 8 "net/http" 9 "net/url" 10 11 "github.com/juju/loggo" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/worker/v3/workertest" 14 gc "gopkg.in/check.v1" 15 16 coretesting "github.com/juju/juju/testing" 17 "github.com/juju/juju/worker/httpserver" 18 ) 19 20 type certSuite struct { 21 workerFixture 22 } 23 24 var _ = gc.Suite(&certSuite{}) 25 26 func testSNIGetter(cert *tls.Certificate) httpserver.SNIGetterFunc { 27 return httpserver.SNIGetterFunc(func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { 28 return cert, nil 29 }) 30 } 31 32 func (s *certSuite) SetUpTest(c *gc.C) { 33 s.workerFixture.SetUpTest(c) 34 tlsConfig := httpserver.InternalNewTLSConfig( 35 "", 36 "https://0.1.2.3/no-autocert-here", 37 nil, 38 testSNIGetter(coretesting.ServerTLSCert), 39 loggo.GetLogger("test"), 40 ) 41 // Copy the root CAs across. 42 tlsConfig.RootCAs = s.config.TLSConfig.RootCAs 43 s.config.TLSConfig = tlsConfig 44 s.config.TLSConfig.ServerName = "juju-apiserver" 45 s.config.Mux.AddHandler("GET", "/hey", http.HandlerFunc(s.handler)) 46 } 47 48 func (s *certSuite) handler(w http.ResponseWriter, req *http.Request) { 49 w.WriteHeader(http.StatusOK) 50 w.Write([]byte("yay")) 51 } 52 53 func (s *certSuite) TestAutocertFailure(c *gc.C) { 54 // We don't have a fake autocert server, but we can at least 55 // smoke test that the autocert path is followed when we try 56 // to connect to a DNS name - the AutocertURL configured 57 // by the testing suite is invalid so it should fail. 58 59 // Dropping the handler returned here disables the challenge 60 // listener. 61 tlsConfig := httpserver.InternalNewTLSConfig( 62 "somewhere.example", 63 "https://0.1.2.3/no-autocert-here", 64 nil, 65 testSNIGetter(coretesting.ServerTLSCert), 66 loggo.GetLogger("test"), 67 ) 68 s.config.TLSConfig = tlsConfig 69 70 worker, err := httpserver.NewWorker(s.config) 71 c.Assert(err, jc.ErrorIsNil) 72 defer workertest.CleanKill(c, worker) 73 74 parsed, err := url.Parse(worker.URL()) 75 c.Assert(err, jc.ErrorIsNil) 76 77 entries := gatherLog(func() { 78 _, err := tls.Dial("tcp", parsed.Host, &tls.Config{ 79 ServerName: "somewhere.example", 80 }) 81 expectedErr := `.*x509: certificate is valid for .*, not somewhere.example` 82 // We can't get an autocert certificate, so we'll fall back to the local certificate 83 // which isn't valid for connecting to somewhere.example. 84 c.Assert(err, gc.ErrorMatches, expectedErr) 85 }) 86 // We will log the failure to get the certificate, thus assuring us that we actually tried. 87 c.Assert(entries, jc.LogMatches, jc.SimpleMessages{{ 88 loggo.INFO, 89 `getting certificate for server name "somewhere.example"`, 90 }, { 91 loggo.ERROR, 92 `.*cannot get autocert certificate for "somewhere.example": Get ["]?https://0\.1\.2\.3/no-autocert-here["]?: .*`, 93 }}) 94 } 95 96 func (s *certSuite) TestAutocertNameMismatch(c *gc.C) { 97 tlsConfig := httpserver.InternalNewTLSConfig( 98 "somewhere.example", 99 "https://0.1.2.3/no-autocert-here", 100 nil, 101 testSNIGetter(coretesting.ServerTLSCert), 102 loggo.GetLogger("test"), 103 ) 104 s.config.TLSConfig = tlsConfig 105 106 worker, err := httpserver.NewWorker(s.config) 107 c.Assert(err, jc.ErrorIsNil) 108 defer workertest.CleanKill(c, worker) 109 110 parsed, err := url.Parse(worker.URL()) 111 c.Assert(err, jc.ErrorIsNil) 112 113 entries := gatherLog(func() { 114 _, err := tls.Dial("tcp", parsed.Host, &tls.Config{ 115 ServerName: "somewhere.else", 116 }) 117 expectedErr := `.*x509: certificate is valid for .*, not somewhere.else` 118 // We can't get an autocert certificate, so we'll fall back to the local certificate 119 // which isn't valid for connecting to somewhere.example. 120 c.Assert(err, gc.ErrorMatches, expectedErr) 121 }) 122 // Check that we logged the mismatch. 123 c.Assert(entries, jc.LogMatches, jc.SimpleMessages{{ 124 loggo.ERROR, 125 `.*cannot get autocert certificate for "somewhere.else": acme/autocert: host "somewhere.else" not configured in HostWhitelist`, 126 }}) 127 } 128 129 func (s *certSuite) TestAutocertNoAutocertDNSName(c *gc.C) { 130 worker, err := httpserver.NewWorker(s.config) 131 c.Assert(err, jc.ErrorIsNil) 132 defer workertest.CleanKill(c, worker) 133 134 parsed, err := url.Parse(worker.URL()) 135 c.Assert(err, jc.ErrorIsNil) 136 137 entries := gatherLog(func() { 138 _, err := tls.Dial("tcp", parsed.Host, &tls.Config{ 139 ServerName: "somewhere.example", 140 }) 141 expectedErr := `.*x509: certificate is valid for .*, not somewhere.example` 142 // We can't get an autocert certificate, so we'll fall back to the local certificate 143 // which isn't valid for connecting to somewhere.example. 144 c.Assert(err, gc.ErrorMatches, expectedErr) 145 }) 146 // Check that we never logged a failure to get the certificate. 147 c.Assert(entries, gc.Not(jc.LogMatches), jc.SimpleMessages{{ 148 loggo.ERROR, 149 `.*cannot get autocert certificate.*`, 150 }}) 151 } 152 153 func gatherLog(f func()) []loggo.Entry { 154 var tw loggo.TestWriter 155 err := loggo.RegisterWriter("test", &tw) 156 if err != nil { 157 panic(err) 158 } 159 defer loggo.RemoveWriter("test") 160 f() 161 return tw.Log() 162 }