Merge lp:~gz/juju-core/1.16_ssl_verification_bootstrap_state_1268913 into lp:juju-core/1.16

Proposed by Martin Packman
Status: Merged
Approved by: Martin Packman
Approved revision: no longer in the source branch.
Merged at revision: 2002
Proposed branch: lp:~gz/juju-core/1.16_ssl_verification_bootstrap_state_1268913
Merge into: lp:juju-core/1.16
Diff against target: 183 lines (+63/-16)
5 files modified
cmd/jujud/bootstrap.go (+1/-1)
environs/httpstorage/storage.go (+4/-1)
provider/common/bootstrap_test.go (+1/-1)
provider/common/state.go (+11/-2)
provider/common/state_test.go (+46/-11)
To merge this branch: bzr merge lp:~gz/juju-core/1.16_ssl_verification_bootstrap_state_1268913
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+203037@code.launchpad.net

Commit message

Obey ssl-hostname-verification for provider-state

When reading the provider-state file from cloud storage on
bootstrap, skip validating the https cert if the config
ssl-hostname-verification is set to false.

https://codereview.appspot.com/56560043/

R=axwalk

Description of the change

Obey ssl-hostname-verification for provider-state

When reading the provider-state file from cloud storage on
bootstrap, skip validating the https cert if the config
ssl-hostname-verification is set to false.

Change simplified to not test the valid cert case, avoiding
the need for a usable testing https server.

https://codereview.appspot.com/56560043/

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Reviewers: mp+203037_code.launchpad.net,

Message:
Please take a look.

Description:
Obey ssl-hostname-verification for provider-state

When reading the provider-state file from cloud storage on
bootstrap, skip validating the https cert if the config
ssl-hostname-verification is set to false.

In order to reuse the existing testing infrastructure for
localStorage, I had to add a new PromotableStorage
interface which does the (overly magic) lookup of the
https endpoint via http head and uses it for *all*
requests, not just the modification ones.

On trunk, I don't see why ClientTLS shouldn't be switched
to just use https all the time.

https://code.launchpad.net/~gz/juju-core/1.16_ssl_verification_bootstrap_state_1268913/+merge/203037

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/56560043/

Affected files (+169, -53 lines):
   A [revision details]
   M cmd/jujud/bootstrap.go
   M environs/httpstorage/backend_test.go
   M environs/httpstorage/storage.go
   M environs/testing/storage.go
   M provider/common/bootstrap_test.go
   M provider/common/state.go
   M provider/common/state_test.go

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/01/27 18:11:55, gz wrote:
> Please take a look.

"On trunk, I don't see why ClientTLS shouldn't be switched
to just use https all the time."

I suppose we could just use HTTPS, but disable host key verification.
The certificate isn't known to clients other than management nodes/CLI.

https://codereview.appspot.com/56560043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

https://codereview.appspot.com/56560043/diff/20001/environs/httpstorage/storage.go
File environs/httpstorage/storage.go (right):

https://codereview.appspot.com/56560043/diff/20001/environs/httpstorage/storage.go#newcode26
environs/httpstorage/storage.go:26: type PromotableStorage interface {
I'm not keen on this. I think a better way to test, rather than further
exposing the guts of this package, would be to use a
net/http/httputil.ReverseProxy.

https://codereview.appspot.com/56560043/diff/20001/provider/common/state.go
File provider/common/state.go (right):

https://codereview.appspot.com/56560043/diff/20001/provider/common/state.go#newcode71
provider/common/state.go:71: func LoadStateFromURL(url string,
disableSSLHostnameVerification bool) (*BootstrapState, error) {
This is fine for now, but I wonder if we shouldn't have a method of
obtaining an http.Client for an environment. We now have HTTP proxy
settings and SSL hostname verification as options. We could centralise
all that and pass an http.Client in here.

https://codereview.appspot.com/56560043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/01/29 07:53:19, axw wrote:
> On 2014/01/27 18:11:55, gz wrote:
> > Please take a look.

> "On trunk, I don't see why ClientTLS shouldn't be switched
> to just use https all the time."

> I suppose we could just use HTTPS, but disable host key verification.
The
> certificate isn't known to clients other than management nodes/CLI.

William pointed out to me that actually, yes, we do have the (public) CA
cert. So we could indeed use HTTPS all the time. It would require us to
somehow get the cert into cloud-init so wget can use it when fetching
tools, and to use it when fetching from storage generally.

https://codereview.appspot.com/56560043/

Revision history for this message
Martin Packman (gz) wrote :

Please take a look.

https://codereview.appspot.com/56560043/diff/20001/environs/httpstorage/storage.go
File environs/httpstorage/storage.go (right):

https://codereview.appspot.com/56560043/diff/20001/environs/httpstorage/storage.go#newcode26
environs/httpstorage/storage.go:26: type PromotableStorage interface {
On 2014/01/29 08:12:59, axw wrote:
> I'm not keen on this. I think a better way to test, rather than
further exposing
> the guts of this package, would be to use a
net/http/httputil.ReverseProxy.

Okay, I'll revert most of this and write something independant.

https://codereview.appspot.com/56560043/diff/20001/provider/common/state.go
File provider/common/state.go (right):

https://codereview.appspot.com/56560043/diff/20001/provider/common/state.go#newcode71
provider/common/state.go:71: func LoadStateFromURL(url string,
disableSSLHostnameVerification bool) (*BootstrapState, error) {
On 2014/01/29 08:12:59, axw wrote:
> This is fine for now, but I wonder if we shouldn't have a method of
obtaining an
> http.Client for an environment. We now have HTTP proxy settings and
SSL hostname
> verification as options. We could centralise all that and pass an
http.Client in
> here.

Yes, on trunk there should be some refactoring like that. There are a
couple of places that want a http.Client obeying thig setting and we may
want more.

https://codereview.appspot.com/56560043/

Revision history for this message
Martin Packman (gz) wrote :
Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/01/30 13:57:41, gz wrote:
> Please take a look.

Thanks, much nicer I think. LGTM.

https://codereview.appspot.com/56560043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

Oops, forgot to publish this.

https://codereview.appspot.com/56560043/diff/60001/provider/common/state_test.go
File provider/common/state_test.go (right):

https://codereview.appspot.com/56560043/diff/60001/provider/common/state_test.go#newcode46
provider/common/state_test.go:46: // testingHTTPServer creates a tempdir
backed https server with invalid certs
s/invalid/self-signed/?

https://codereview.appspot.com/56560043/

Revision history for this message
Martin Packman (gz) wrote :

Please take a look.

https://codereview.appspot.com/56560043/diff/60001/provider/common/state_test.go
File provider/common/state_test.go (right):

https://codereview.appspot.com/56560043/diff/60001/provider/common/state_test.go#newcode46
provider/common/state_test.go:46: // testingHTTPServer creates a tempdir
backed https server with invalid certs
On 2014/01/31 01:57:27, axw wrote:
> s/invalid/self-signed/?

Good call. And really, it's just without certs right?

https://codereview.appspot.com/56560043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/01/31 12:33:34, gz wrote:
> Please take a look.

https://codereview.appspot.com/56560043/diff/60001/provider/common/state_test.go
> File provider/common/state_test.go (right):

https://codereview.appspot.com/56560043/diff/60001/provider/common/state_test.go#newcode46
> provider/common/state_test.go:46: // testingHTTPServer creates a
tempdir backed
> https server with invalid certs
> On 2014/01/31 01:57:27, axw wrote:
> > s/invalid/self-signed/?

> Good call. And really, it's just without certs right?

No, there's a certificate (there has to be), it's just self-signed. The
SSL certificate verification fails because the CA is unknown.

https://codereview.appspot.com/56560043/

Revision history for this message
Martin Packman (gz) wrote :

On 2014/01/31 12:52:59, axw wrote:

> No, there's a certificate (there has to be), it's just self-signed.
The SSL
> certificate verification fails because the CA is unknown.

Hm, but we're not supplying it, so presumably the httptest server stuff
has its own one?

https://codereview.appspot.com/56560043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/02/01 14:54:54, gz wrote:
> On 2014/01/31 12:52:59, axw wrote:
> >
> > No, there's a certificate (there has to be), it's just self-signed.
The SSL
> > certificate verification fails because the CA is unknown.

> Hm, but we're not supplying it, so presumably the httptest server
stuff has its
> own one?

Yes:
http://golang.org/src/pkg/net/http/httptest/server.go?s=3153:3180#L107

https://codereview.appspot.com/56560043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/jujud/bootstrap.go'
2--- cmd/jujud/bootstrap.go 2013-09-30 19:40:06 +0000
3+++ cmd/jujud/bootstrap.go 2014-01-31 12:32:29 +0000
4@@ -96,7 +96,7 @@
5 return fmt.Errorf("cannot read provider-state-url file: %v", err)
6 }
7 stateInfoURL := strings.Split(string(data), "\n")[0]
8- bsState, err := common.LoadStateFromURL(stateInfoURL)
9+ bsState, err := common.LoadStateFromURL(stateInfoURL, !cfg.SSLHostnameVerification())
10 if err != nil {
11 return fmt.Errorf("cannot load state from URL %q (read from %q): %v", stateInfoURL, providerStateURLFile, err)
12 }
13
14=== modified file 'environs/httpstorage/storage.go'
15--- environs/httpstorage/storage.go 2013-10-02 05:46:56 +0000
16+++ environs/httpstorage/storage.go 2014-01-31 12:32:29 +0000
17@@ -64,12 +64,15 @@
18 }
19
20 func (s *localStorage) getHTTPSBaseURL() (string, error) {
21- url, _ := s.URL("/") // never fails
22+ url, _ := s.URL("") // never fails
23 resp, err := s.client.Head(url)
24 if err != nil {
25 return "", err
26 }
27 resp.Body.Close()
28+ if resp.StatusCode != http.StatusOK {
29+ return "", fmt.Errorf("Could not access file storage: %v %s", url, resp.Status)
30+ }
31 httpsURL, err := resp.Location()
32 if err != nil {
33 return "", err
34
35=== modified file 'provider/common/bootstrap_test.go'
36--- provider/common/bootstrap_test.go 2013-10-02 00:29:29 +0000
37+++ provider/common/bootstrap_test.go 2014-01-31 12:32:29 +0000
38@@ -151,7 +151,7 @@
39 err := common.Bootstrap(env, constraints.Value{}, nil)
40 c.Assert(err, gc.IsNil)
41
42- savedState, err := common.LoadStateFromURL(checkURL)
43+ savedState, err := common.LoadStateFromURL(checkURL, false)
44 c.Assert(err, gc.IsNil)
45 c.Assert(savedState, gc.DeepEquals, &common.BootstrapState{
46 StateInstances: []instance.Id{instance.Id(checkInstanceId)},
47
48=== modified file 'provider/common/state.go'
49--- provider/common/state.go 2013-10-01 12:03:33 +0000
50+++ provider/common/state.go 2014-01-31 12:32:29 +0000
51@@ -21,6 +21,7 @@
52 "launchpad.net/juju-core/log"
53 "launchpad.net/juju-core/state"
54 "launchpad.net/juju-core/state/api"
55+ "launchpad.net/juju-core/utils"
56 )
57
58 // StateFile is the name of the file where the provider's state is stored.
59@@ -67,11 +68,19 @@
60 }
61
62 // LoadStateFromURL reads state from the given URL.
63-func LoadStateFromURL(url string) (*BootstrapState, error) {
64- resp, err := http.Get(url)
65+func LoadStateFromURL(url string, disableSSLHostnameVerification bool) (*BootstrapState, error) {
66+ client := http.DefaultClient
67+ if disableSSLHostnameVerification {
68+ logger.Infof("hostname SSL verification disabled")
69+ client = utils.GetNonValidatingHTTPClient()
70+ }
71+ resp, err := client.Get(url)
72 if err != nil {
73 return nil, err
74 }
75+ if resp.StatusCode != http.StatusOK {
76+ return nil, fmt.Errorf("could not load state from url: %v %s", url, resp.Status)
77+ }
78 return loadState(resp.Body)
79 }
80
81
82=== modified file 'provider/common/state_test.go'
83--- provider/common/state_test.go 2013-10-02 10:39:12 +0000
84+++ provider/common/state_test.go 2014-01-31 12:32:29 +0000
85@@ -4,8 +4,10 @@
86 package common_test
87
88 import (
89- "bytes"
90 "io/ioutil"
91+ "net/http"
92+ "net/http/httptest"
93+ "path/filepath"
94
95 gc "launchpad.net/gocheck"
96 "launchpad.net/goyaml"
97@@ -30,10 +32,26 @@
98 AddCleanup(testbase.CleanupFunc)
99 }
100
101-func newStorage(suite cleaner, c *gc.C) storage.Storage {
102- closer, stor, _ := envtesting.CreateLocalTestStorage(c)
103+func newStorageWithDataDir(suite cleaner, c *gc.C) (stor storage.Storage, dataDir string) {
104+ closer, stor, dataDir := envtesting.CreateLocalTestStorage(c)
105 suite.AddCleanup(func(*gc.C) { closer.Close() })
106- return stor
107+ return
108+}
109+
110+func newStorage(suite cleaner, c *gc.C) (stor storage.Storage) {
111+ stor, _ = newStorageWithDataDir(suite, c)
112+ return
113+}
114+
115+// testingHTTPServer creates a tempdir backed https server without certs
116+func testingHTTPSServer(suite cleaner, c *gc.C) (baseURL string, dataDir string) {
117+ dataDir = c.MkDir()
118+ mux := http.NewServeMux()
119+ mux.Handle("/", http.FileServer(http.Dir(dataDir)))
120+ server := httptest.NewTLSServer(mux)
121+ suite.AddCleanup(func(*gc.C) { server.Close() })
122+ baseURL = server.URL
123+ return
124 }
125
126 func (suite *StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) {
127@@ -72,32 +90,49 @@
128 c.Check(content, gc.DeepEquals, marshaledState)
129 }
130
131-func (suite *StateSuite) setUpSavedState(c *gc.C, stor storage.Storage) common.BootstrapState {
132+func (suite *StateSuite) setUpSavedState(c *gc.C, dataDir string) common.BootstrapState {
133 arch := "amd64"
134 state := common.BootstrapState{
135 StateInstances: []instance.Id{instance.Id("an-instance-id")},
136 Characteristics: []instance.HardwareCharacteristics{{Arch: &arch}}}
137 content, err := goyaml.Marshal(state)
138 c.Assert(err, gc.IsNil)
139- err = stor.Put(common.StateFile, ioutil.NopCloser(bytes.NewReader(content)), int64(len(content)))
140+ err = ioutil.WriteFile(filepath.Join(dataDir, common.StateFile), []byte(content), 0644)
141 c.Assert(err, gc.IsNil)
142 return state
143 }
144
145 func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) {
146- storage := newStorage(suite, c)
147- state := suite.setUpSavedState(c, storage)
148+ storage, dataDir := newStorageWithDataDir(suite, c)
149+ state := suite.setUpSavedState(c, dataDir)
150 storedState, err := common.LoadState(storage)
151 c.Assert(err, gc.IsNil)
152 c.Check(*storedState, gc.DeepEquals, state)
153 }
154
155 func (suite *StateSuite) TestLoadStateFromURLReadsStateFile(c *gc.C) {
156- stor := newStorage(suite, c)
157- state := suite.setUpSavedState(c, stor)
158+ stor, dataDir := newStorageWithDataDir(suite, c)
159+ state := suite.setUpSavedState(c, dataDir)
160 url, err := stor.URL(common.StateFile)
161 c.Assert(err, gc.IsNil)
162- storedState, err := common.LoadStateFromURL(url)
163+ storedState, err := common.LoadStateFromURL(url, false)
164+ c.Assert(err, gc.IsNil)
165+ c.Check(*storedState, gc.DeepEquals, state)
166+}
167+
168+func (suite *StateSuite) TestLoadStateFromURLBadCert(c *gc.C) {
169+ baseURL, _ := testingHTTPSServer(suite, c)
170+ url := baseURL + "/" + common.StateFile
171+ storedState, err := common.LoadStateFromURL(url, false)
172+ c.Assert(err, gc.ErrorMatches, ".*/provider-state:.* certificate signed by unknown authority")
173+ c.Assert(storedState, gc.IsNil)
174+}
175+
176+func (suite *StateSuite) TestLoadStateFromURLBadCertPermitted(c *gc.C) {
177+ baseURL, dataDir := testingHTTPSServer(suite, c)
178+ state := suite.setUpSavedState(c, dataDir)
179+ url := baseURL + "/" + common.StateFile
180+ storedState, err := common.LoadStateFromURL(url, true)
181 c.Assert(err, gc.IsNil)
182 c.Check(*storedState, gc.DeepEquals, state)
183 }

Subscribers

People subscribed via source and target branches

to all changes: