Skip to content

Commit 47fd6ba

Browse files
committed
Fix for LP1921557 sni in Juju login.
Juju login has been failing with recent changes to Juju's certificate management. Specifically there are two problems that have come up. The first is the Juju controller selecting the right certificate when ip connections are being initiated that don't have an SNI name set. This has been fixed by returning Juju's ip certificate with empty SNI names. The second is Juju returning the CA cert in it's certificate chain. Normally it's not considered valid to return a root CA in a chain but for the way Juju works when logging in it needs to in order to trust the CA and add it to the config file.
1 parent 9de8d59 commit 47fd6ba

File tree

6 files changed

+64
-17
lines changed

6 files changed

+64
-17
lines changed

cmd/juju/user/login.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"crypto/x509"
99
"encoding/pem"
1010
"fmt"
11+
"net"
1112
"net/http"
1213
"os"
1314
"strings"
@@ -362,10 +363,16 @@ func (c *loginCommand) publicControllerLogin(
362363
dialOpts.BakeryClient.AddInteractor(i)
363364
}
364365

366+
sniHost, _, err := net.SplitHostPort(host)
367+
if err != nil {
368+
return nil, errors.Annotatef(err, "getting sni host from host %q", host)
369+
}
370+
365371
return apiOpen(&c.CommandBase, &api.Info{
366-
Tag: tag,
367-
Password: d.Password,
368-
Addrs: []string{host},
372+
Tag: tag,
373+
Password: d.Password,
374+
Addrs: []string{host},
375+
SNIHostName: sniHost,
369376
}, dialOpts)
370377
}
371378
conn, accountDetails, err := c.login(ctx, currentAccountDetails, dial)

pki/authority.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import (
1818
)
1919

2020
const (
21-
DefaultLeafGroup = "controller"
21+
DefaultLeafGroup = "controller"
22+
ControllerIPLeafGroup = "controllerip"
2223
)
2324

2425
// Authority represents a secure means of issuing groups of common interest
@@ -80,6 +81,14 @@ func (a *DefaultAuthority) Chain() []*x509.Certificate {
8081
return a.authority.Chain()
8182
}
8283

84+
func (a *DefaultAuthority) ChainWithAuthority() []*x509.Certificate {
85+
chain := a.authority.Chain()
86+
if chain == nil {
87+
chain = []*x509.Certificate{}
88+
}
89+
return append(chain, a.authority.Certificate())
90+
}
91+
8392
// leafMaker is responsible for providing a method to make new leafs after
8493
// request signing.
8594
func (a *DefaultAuthority) leafMaker(groupKey string) LeafMaker {
@@ -104,11 +113,11 @@ func (a *DefaultAuthority) LeafRequestForGroup(group string) LeafRequest {
104113
defer a.leafSignerMutex.Unlock()
105114
if a.leafSigner != nil {
106115
return NewDefaultLeafRequestWithSigner(subject, a.leafSigner,
107-
NewDefaultRequestSigner(a.Certificate(), a.Chain(), a.Signer()),
116+
NewDefaultRequestSigner(a.Certificate(), a.ChainWithAuthority(), a.Signer()),
108117
a.leafMaker(groupKey))
109118
}
110119
return NewDefaultLeafRequest(subject,
111-
NewDefaultRequestSigner(a.Certificate(), a.Chain(), a.Signer()),
120+
NewDefaultRequestSigner(a.Certificate(), a.ChainWithAuthority(), a.Signer()),
112121
a.leafMaker(groupKey))
113122
}
114123

pki/authority_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ func (a *AuthoritySuite) TestLeafRequest(c *gc.C) {
7676
c.Assert(leaf.Certificate().IPAddresses, jc.DeepEquals, ipAddresses)
7777
}
7878

79+
func (a *AuthoritySuite) TestLeafRequestChain(c *gc.C) {
80+
authority, err := pki.NewDefaultAuthority(a.ca, a.signer)
81+
c.Assert(err, jc.ErrorIsNil)
82+
dnsNames := []string{"test.juju.is"}
83+
ipAddresses := []net.IP{net.ParseIP("fe80:abcd::1")}
84+
leaf, err := authority.LeafRequestForGroup("testgroup").
85+
AddDNSNames(dnsNames...).
86+
AddIPAddresses(ipAddresses...).
87+
Commit()
88+
89+
chain := leaf.Chain()
90+
c.Assert(len(chain), gc.Equals, 1)
91+
c.Assert(chain[0], jc.DeepEquals, authority.Certificate())
92+
}
93+
7994
func (a *AuthoritySuite) TestLeafFromPem(c *gc.C) {
8095
authority, err := pki.NewDefaultAuthority(a.ca, a.signer)
8196
c.Assert(err, jc.ErrorIsNil)

pki/tls/sni.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,28 @@ type Logger interface {
2121
func AuthoritySNITLSGetter(authority pki.Authority, logger Logger) func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
2222
return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
2323
logger.Debugf("received tls client hello for server name %s", hello.ServerName)
24+
2425
var cert *tls.Certificate
25-
authority.LeafRange(func(leaf pki.Leaf) bool {
26-
if err := hello.SupportsCertificate(leaf.TLSCertificate()); err == nil {
27-
cert = leaf.TLSCertificate()
28-
return false
26+
27+
// NOTE: This was added in response to bug lp:1921557. If we get an
28+
// empty server name here we assume the the connection is being made
29+
// with an ip address as the host.
30+
if hello.ServerName == "" {
31+
logger.Debugf("tls client hello server name is empty. Attempting to provide ip address certificate")
32+
leaf, err := authority.LeafForGroup(pki.ControllerIPLeafGroup)
33+
if err != nil && !errors.IsNotFound(err) {
34+
return nil, errors.Annotate(err, "fetching ip address certificate")
2935
}
30-
return true
31-
})
36+
cert = leaf.TLSCertificate()
37+
} else {
38+
authority.LeafRange(func(leaf pki.Leaf) bool {
39+
if err := hello.SupportsCertificate(leaf.TLSCertificate()); err == nil {
40+
cert = leaf.TLSCertificate()
41+
return false
42+
}
43+
return true
44+
})
45+
}
3246

3347
if cert == nil {
3448
logger.Debugf("no matching certificate found for tls client hello %s, using default certificate", hello.ServerName)

pki/tls/sni_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ func (s *SNISuite) TestAuthorityTLSGetter(c *gc.C) {
8989
Group: pki.DefaultLeafGroup,
9090
ServerName: "doesnotexist.juju.example",
9191
},
92+
{
93+
// Regression test for LP1921557
94+
ExpectedGroup: pki.ControllerIPLeafGroup,
95+
Group: pki.ControllerIPLeafGroup,
96+
ServerName: "",
97+
},
9298
}
9399

94100
for _, test := range tests {

worker/certupdater/certupdater.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ import (
1717
"github.com/juju/juju/watcher/legacy"
1818
)
1919

20-
const (
21-
ControllerIPLeafGroup = "controllerip"
22-
)
23-
2420
var (
2521
logger = loggo.GetLogger("juju.worker.certupdater")
2622
)
@@ -112,7 +108,7 @@ func (c *CertificateUpdater) updateCertificate(addresses network.SpaceAddresses)
112108
logger.Debugf("new machine addresses: %#v", addresses)
113109
c.addresses = addresses
114110

115-
request := c.authority.LeafRequestForGroup(ControllerIPLeafGroup)
111+
request := c.authority.LeafRequestForGroup(pki.ControllerIPLeafGroup)
116112

117113
for _, addr := range addresses {
118114
if addr.Value == "localhost" {

0 commit comments

Comments
 (0)