Skip to content

Commit

Permalink
Merge pull request juju#19031 from kian99/3.6-to-main
Browse files Browse the repository at this point in the history
juju#19031

This PR merges `3.6` to `main` including the changes from the following PRs:
- juju#18965
- juju#18990
- juju#18989
- juju#18829
- juju#19024
- juju#19027

Notable conflicts were in juju#18829 which introduced virtual host keys. These changes were dropped by running `git merge e19464a -X ours` and then removing all further references of virtual host keys. This change will need to be separately implemented in `main` using dqlite.
  • Loading branch information
jujubot authored Feb 26, 2025
2 parents a28e28f + 5c911a8 commit 9001eab
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 32 deletions.
4 changes: 2 additions & 2 deletions cmd/jujud-controller/agent/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,11 +417,11 @@ func ensureSSHServerHostKey(args *instancecfg.StateInitializationParams) error {
return nil
}
// Generate the embedded SSH server host key and store it within StateInitializationParams.
hostKey, err := pkissh.GenerateED25519KeyString()
hostKey, err := pkissh.NewMarshalledED25519()
if err != nil {
return errors.Annotatef(err, "failed to ensure ssh server host key")
}
args.SSHServerHostKey = hostKey
args.SSHServerHostKey = string(hostKey)
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ require (
go.opentelemetry.io/otel/trace v1.32.0
go.uber.org/goleak v1.3.0
go.uber.org/mock v0.5.0
golang.org/x/crypto v0.33.0
golang.org/x/crypto v0.35.0
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.26.0
golang.org/x/sync v0.11.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -791,8 +791,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
Expand Down
38 changes: 25 additions & 13 deletions internal/pki/ssh/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"encoding/pem"

"github.com/juju/errors"
"golang.org/x/crypto/ssh"
gossh "golang.org/x/crypto/ssh"
)

type KeyProfile func() (crypto.PrivateKey, error)
Expand Down Expand Up @@ -49,6 +49,24 @@ func ED25519() (crypto.PrivateKey, error) {
return pk, err
}

// MarshalPrivateKey marshals a private key to a PEM encoded byte slice.
func MarshalPrivateKey(privateKey crypto.PrivateKey) ([]byte, error) {
pemKey, err := gossh.MarshalPrivateKey(privateKey, "")
if err != nil {
return nil, errors.Annotate(err, "failed to marshal private key")
}
return pem.EncodeToMemory(pemKey), nil
}

// UnmarshalPrivateKey unmarshals a private key from a PEM encoded byte slice.
func UnmarshalPrivateKey(data []byte) (crypto.PrivateKey, error) {
privateKey, err := gossh.ParseRawPrivateKey(data)
if err != nil {
return nil, errors.Annotate(err, "failed to unmarshal private key")
}
return privateKey, nil
}

var hostKeyProfiles = []KeyProfile{
RSA2048,
ECDSAP256,
Expand All @@ -69,18 +87,12 @@ func GenerateHostKeys() ([]crypto.PrivateKey, error) {
return res, nil
}

// GenerateED25519KeyString generates a new ED25519 private key and returns it as a PEM encoded string.
func GenerateED25519KeyString() (string, error) {
_, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return "", errors.Annotate(err, "failed to generate ED25519 key")
}

pemKey, err := ssh.MarshalPrivateKey(privateKey, "")
// NewMarshalledED25519 is a convenience function wrapping a call to
// create a new ED25519 private key and then marhsalling the result.
func NewMarshalledED25519() ([]byte, error) {
privateKey, err := ED25519()
if err != nil {
return "", errors.Annotate(err, "failed to marshal private key")
return nil, err
}

pemString := string(pem.EncodeToMemory(pemKey))
return pemString, nil
return MarshalPrivateKey(privateKey)
}
25 changes: 22 additions & 3 deletions internal/pki/ssh/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package ssh_test

import (
"crypto"
"crypto/ed25519"

jc "github.com/juju/testing/checkers"
gossh "golang.org/x/crypto/ssh"
Expand Down Expand Up @@ -55,11 +56,29 @@ func (s *KeySuite) TestGenerateHostKeys(c *gc.C) {
}
}

func (s *KeySuite) TestGenerateED25519KeyString(c *gc.C) {
keyStr, err := ssh.GenerateED25519KeyString()
func (s *KeySuite) TestKeyMarshalling(c *gc.C) {
privateKey, err := ssh.ED25519()
c.Assert(err, gc.IsNil)

signer, err := gossh.ParsePrivateKey([]byte(keyStr))
want, ok := privateKey.(ed25519.PrivateKey)
c.Assert(ok, gc.Equals, true)

data, err := ssh.MarshalPrivateKey(privateKey)
c.Assert(err, gc.IsNil)

unmarshalledKey, err := ssh.UnmarshalPrivateKey(data)
c.Assert(err, gc.IsNil)
got, ok := unmarshalledKey.(*ed25519.PrivateKey)
c.Assert(ok, gc.Equals, true)

c.Assert(want, gc.DeepEquals, *got)
}

func (s *KeySuite) TestGenerateMarshalledED25519Key(c *gc.C) {
keyStr, err := ssh.NewMarshalledED25519()
c.Assert(err, gc.IsNil)

signer, err := gossh.ParsePrivateKey(keyStr)
c.Assert(err, gc.IsNil)

c.Assert(signer.PublicKey().Type(), gc.Equals, "ssh-ed25519")
Expand Down
5 changes: 5 additions & 0 deletions internal/worker/peergrouper/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,11 @@ func (w *pgWorker) updateReplicaSet() (map[string]*replicaset.Member, error) {
m := desired.members[id]
ms = append(ms, *m)
}
// In the case of a replica set change after a primary step
// down, the session needs to be refreshed on every other node
// in the replica set, so that the socket addresses are updated
// to the new primary.
w.config.MongoSession.Refresh()
if err := w.config.MongoSession.Set(ms); err != nil {
return nil, errors.WithType(err, replicaSetError)
}
Expand Down
19 changes: 10 additions & 9 deletions internal/worker/sshserver/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"crypto/rsa"

"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
"github.com/juju/worker/v4/workertest"
"go.uber.org/mock/gomock"
"golang.org/x/crypto/ssh"
Expand All @@ -31,10 +32,10 @@ func (s *sshServerSuite) SetUpSuite(c *gc.C) {

// Setup user signer
userKey, err := rsa.GenerateKey(rand.Reader, 2048)
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)

userSigner, err := ssh.NewSignerFromKey(userKey)
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)

s.userSigner = userSigner
}
Expand All @@ -55,13 +56,13 @@ func (s *sshServerSuite) TestSSHServer(c *gc.C) {
Logger: loggertesting.WrapCheckLog(c),
Listener: listener,
})
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)
defer workertest.DirtyKill(c, server)
workertest.CheckAlive(c, server)

// Dial the in-memory listener
conn, err := listener.Dial()
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)

// Open a client connection
jumpConn, chans, terminatingReqs, err := ssh.NewClientConn(
Expand All @@ -74,12 +75,12 @@ func (s *sshServerSuite) TestSSHServer(c *gc.C) {
},
},
)
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)

// Open jump connection
client := ssh.NewClient(jumpConn, chans, terminatingReqs)
tunnel, err := client.Dial("tcp", "1.postgresql.8419cd78-4993-4c3a-928e-c646226beeee.juju.local:20")
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)

// Now with this opened direct-tcpip channel, open a session connection
terminatingClientConn, terminatingClientChan, terminatingReqs, err := ssh.NewClientConn(
Expand All @@ -92,14 +93,14 @@ func (s *sshServerSuite) TestSSHServer(c *gc.C) {
ssh.PublicKeys(s.userSigner),
},
})
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)

terminatingClient := ssh.NewClient(terminatingClientConn, terminatingClientChan, terminatingReqs)
terminatingSession, err := terminatingClient.NewSession()
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)

output, err := terminatingSession.CombinedOutput("")
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)
c.Assert(string(output), gc.Equals, "Your final destination is: 1.postgresql.8419cd78-4993-4c3a-928e-c646226beeee.juju.local as user: ubuntu\n")

// Server isn't gracefully closed, it's forcefully closed. All connections ended
Expand Down
4 changes: 2 additions & 2 deletions internal/worker/sshserver/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (s *workerSuite) TestSSHServerWrapperWorkerCanBeKilled(c *gc.C) {
},
}
w, err := sshserver.NewServerWrapperWorker(cfg)
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)
defer workertest.DirtyKill(c, w)

// Check all workers alive properly.
Expand Down Expand Up @@ -127,7 +127,7 @@ func (s *workerSuite) TestSSHServerWrapperWorkerRestartsServerWorker(c *gc.C) {
},
}
w, err := sshserver.NewServerWrapperWorker(cfg)
c.Assert(err, gc.IsNil)
c.Assert(err, jc.ErrorIsNil)
defer workertest.DirtyKill(c, w)

// Check all workers alive properly.
Expand Down

0 comments on commit 9001eab

Please sign in to comment.