Install public key on dryad 42/181342/8
authorAleksander Mistewicz <a.mistewicz@samsung.com>
Tue, 12 Jun 2018 08:42:30 +0000 (10:42 +0200)
committerAleksander Mistewicz <a.mistewicz@samsung.com>
Fri, 3 Aug 2018 11:36:41 +0000 (13:36 +0200)
This patch changes interface and communication between boruta server and
dryads. Key is generated on boruta server, public part is installed on
dryad and private part is stored internally. It is a preparation for
using user's public keys provided by an external service.

Change-Id: Ic6fb087aba02553c6b2b8f7cc13cc6bd67eff36a
Signed-off-by: Aleksander Mistewicz <a.mistewicz@samsung.com>
boruta.go
dryad/key.go
dryad/key_test.go [deleted file]
dryad/rusalka.go
dryad/rusalka_test.go
dryad/user.go
rpc/dryad/dryad.go
workers/dryadclientmanager_mock_test.go
workers/worker_list_test.go
workers/workers.go

index c4a7e9b..2f87ea5 100644 (file)
--- a/boruta.go
+++ b/boruta.go
@@ -232,7 +232,7 @@ type Dryad interface {
        // Prepare creates appropriate user, generates RSA key, installs public key
        // so that it can be used for SSH authentication and returns private key.
        // It removes current instance of the user, etc.
-       Prepare() (key *rsa.PrivateKey, err error)
+       Prepare(key *rsa.PublicKey) (err error)
        // Healthcheck tests Dryad for system state, STM functions and state on MuxPi.
        // It may cause Dryad to call SetFail of Worker interface if the problem detected is critical.
        Healthcheck() (err error)
index 83f322b..3881709 100644 (file)
@@ -17,8 +17,6 @@
 package dryad
 
 import (
-       "crypto/rand"
-       "crypto/rsa"
        "os"
        "path"
        "strconv"
@@ -26,10 +24,6 @@ import (
        "golang.org/x/crypto/ssh"
 )
 
-// sizeRSA is a length of the RSA key.
-// It is experimentally chosen value as it is the longest key while still being fast to generate.
-const sizeRSA = 1024
-
 // installPublicKey marshals and stores key in a proper location to be read by ssh daemon.
 func installPublicKey(key ssh.PublicKey, homedir, uid, gid string) error {
        sshDir := path.Join(homedir, ".ssh")
@@ -67,21 +61,3 @@ func updateOwnership(key *os.File, sshDir, uidStr, gidStr string) (err error) {
        }
        return key.Chown(uid, gid)
 }
-
-// generateAndInstallKey generates a new RSA key pair, installs the public part,
-// changes its owner, and returns the private part.
-func generateAndInstallKey(homedir, uid, gid string) (*rsa.PrivateKey, error) {
-       key, err := rsa.GenerateKey(rand.Reader, sizeRSA)
-       if err != nil {
-               return nil, err
-       }
-       sshPubKey, err := ssh.NewPublicKey(&key.PublicKey)
-       if err != nil {
-               return nil, err
-       }
-       err = installPublicKey(sshPubKey, homedir, uid, gid)
-       if err != nil {
-               return nil, err
-       }
-       return key, nil
-}
diff --git a/dryad/key_test.go b/dryad/key_test.go
deleted file mode 100644 (file)
index c4dcf1e..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License
- */
-
-package dryad
-
-import (
-       "crypto/rand"
-       "crypto/rsa"
-       "strconv"
-
-       . "github.com/onsi/ginkgo"
-       . "github.com/onsi/gomega"
-)
-
-var _ = Describe("key generator", func() {
-       generate := func(size int) {
-               _, err := rsa.GenerateKey(rand.Reader, size)
-               Expect(err).ToNot(HaveOccurred())
-       }
-
-       Measure("2048 should be fast", func(b Benchmarker) {
-               for _, size := range []int{512, 1024, 2048, 4096} {
-                       b.Time(strconv.Itoa(size), func() {
-                               generate(size)
-                       })
-               }
-       }, 2)
-})
index 8782b0b..7549308 100644 (file)
@@ -26,6 +26,7 @@ import (
 
        . "git.tizen.org/tools/boruta"
        "git.tizen.org/tools/muxpi/sw/nanopi/stm"
+       "golang.org/x/crypto/ssh"
 )
 
 // Rusalka implements Dryad interface. It is intended to be used on NanoPi connected to MuxPi.
@@ -61,7 +62,7 @@ func (r *Rusalka) PutInMaintenance(msg string) error {
 }
 
 // Prepare is part of implementation of Dryad interface. Call to Prepare stops LED blinking.
-func (r *Rusalka) Prepare() (key *rsa.PrivateKey, err error) {
+func (r *Rusalka) Prepare(key *rsa.PublicKey) (err error) {
        // Stop maintenance.
        if r.cancelMaintenance != nil {
                r.cancelMaintenance()
@@ -70,19 +71,21 @@ func (r *Rusalka) Prepare() (key *rsa.PrivateKey, err error) {
        // Remove/Add user.
        err = r.dryadUser.delete()
        if err != nil {
-               return nil, fmt.Errorf("user removal failed: %s", err)
+               return fmt.Errorf("user removal failed: %s", err)
        }
        err = r.dryadUser.add()
        if err != nil {
-               return nil, fmt.Errorf("user creation failed: %s", err)
+               return fmt.Errorf("user creation failed: %s", err)
        }
        // Verify user's existance.
        err = r.dryadUser.update()
        if err != nil {
-               return nil, fmt.Errorf("user information update failed: %s", err)
+               return fmt.Errorf("user information update failed: %s", err)
        }
-       // Prepare SSH access.
-       return r.dryadUser.generateAndInstallKey()
+       // Prepare SSH access (it can't fail as key is of type rsa.PublicKey).
+       sshPubKey, _ := ssh.NewPublicKey(key)
+       // TODO: use ssh.PublicKey instead.
+       return r.dryadUser.generateAndInstallKey(sshPubKey)
 }
 
 // Healthcheck is part of implementation of Dryad interface.
index e479c55..f3799af 100644 (file)
@@ -19,6 +19,8 @@ package dryad
 //go:generate mockgen -destination=muxpi_mock_test.go -package dryad git.tizen.org/tools/muxpi/sw/nanopi/stm Interface
 
 import (
+       "crypto/rand"
+       "crypto/rsa"
        "crypto/x509"
        "encoding/pem"
        "os"
@@ -82,7 +84,9 @@ var _ = Describe("Rusalka", func() {
                        Skip("must be run as root")
                }
 
-               key, err := d.Prepare()
+               key, err := rsa.GenerateKey(rand.Reader, 1024)
+               Expect(err).ToNot(HaveOccurred())
+               err = d.Prepare(&key.PublicKey)
                Expect(err).ToNot(HaveOccurred())
                Expect(sshDir).To(BeADirectory())
                Expect(authorizedKeysFile).To(BeARegularFile())
index dc2f48e..ef80812 100644 (file)
 package dryad
 
 import (
-       "crypto/rsa"
        "fmt"
        "os/exec"
        "os/user"
        "strings"
        "syscall"
+
+       "golang.org/x/crypto/ssh"
 )
 
 var (
@@ -138,6 +139,6 @@ func (bu *borutaUser) update() (err error) {
 
 // generateAndInstallKey calls generateAndInstallKey with parameters retrieved from the user field
 // of borutaUser structure. This filed must be set before call to this function by update() method.
-func (bu *borutaUser) generateAndInstallKey() (*rsa.PrivateKey, error) {
-       return generateAndInstallKey(bu.user.HomeDir, bu.user.Uid, bu.user.Gid)
+func (bu *borutaUser) generateAndInstallKey(key ssh.PublicKey) error {
+       return installPublicKey(key, bu.user.HomeDir, bu.user.Uid, bu.user.Gid)
 }
index d1a6796..2ee7df3 100644 (file)
@@ -40,16 +40,16 @@ func (s *DryadService) PutInMaintenance(request *DryadPutInMaintenanceRequest, r
 
 // DryadPrepareRequest is a helper structure for Prepare method.
 type DryadPrepareRequest struct {
+       Key *rsa.PublicKey
 }
 
 // DryadPrepareResponse is a helper structure for Prepare method.
 type DryadPrepareResponse struct {
-       Key *rsa.PrivateKey
 }
 
 // Prepare is RPC implementation of Prepare calling it.
 func (s *DryadService) Prepare(request *DryadPrepareRequest, response *DryadPrepareResponse) (err error) {
-       response.Key, err = s.impl.Prepare()
+       err = s.impl.Prepare(request.Key)
        return
 }
 
@@ -97,11 +97,11 @@ func (_c *DryadClient) PutInMaintenance(msg string) (err error) {
 }
 
 // Prepare is part of implementation of Dryad calling corresponding method on RPC server.
-func (_c *DryadClient) Prepare() (key *rsa.PrivateKey, err error) {
-       _request := &DryadPrepareRequest{}
+func (_c *DryadClient) Prepare(key *rsa.PublicKey) (err error) {
+       _request := &DryadPrepareRequest{key}
        _response := &DryadPrepareResponse{}
        err = _c.client.Call("Dryad.Prepare", _request, _response)
-       return _response.Key, err
+       return err
 }
 
 // Healthcheck is part of implementation of Dryad calling corresponding method on RPC server.
index 8db3a0d..fb70ae3 100644 (file)
@@ -70,16 +70,15 @@ func (mr *MockDryadClientManagerMockRecorder) Healthcheck() *gomock.Call {
 }
 
 // Prepare mocks base method
-func (m *MockDryadClientManager) Prepare() (*rsa.PrivateKey, error) {
-       ret := m.ctrl.Call(m, "Prepare")
-       ret0, _ := ret[0].(*rsa.PrivateKey)
-       ret1, _ := ret[1].(error)
-       return ret0, ret1
+func (m *MockDryadClientManager) Prepare(arg0 *rsa.PublicKey) error {
+       ret := m.ctrl.Call(m, "Prepare", arg0)
+       ret0, _ := ret[0].(error)
+       return ret0
 }
 
 // Prepare indicates an expected call of Prepare
-func (mr *MockDryadClientManagerMockRecorder) Prepare() *gomock.Call {
-       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockDryadClientManager)(nil).Prepare))
+func (mr *MockDryadClientManagerMockRecorder) Prepare(arg0 interface{}) *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockDryadClientManager)(nil).Prepare), arg0)
 }
 
 // PutInMaintenance mocks base method
index 6c17bc2..9264206 100644 (file)
@@ -16,6 +16,8 @@
 
 package workers
 
+//go:generate mockgen -package workers -destination=dryadclientmanager_mock_test.go -write_package_comment=false -mock_names ClientManager=MockDryadClientManager git.tizen.org/tools/boruta/rpc/dryad ClientManager
+
 import (
        "crypto/rand"
        "crypto/rsa"
@@ -48,6 +50,7 @@ var _ = Describe("WorkerList", func() {
                IP: dryadAddr.IP,
        }
        BeforeEach(func() {
+               sizeRSA = 256
                wl = NewWorkerList()
        })
 
@@ -339,7 +342,6 @@ var _ = Describe("WorkerList", func() {
                                var ctrl *gomock.Controller
                                var dcm *MockDryadClientManager
                                ip := net.IPv4(2, 4, 6, 8)
-                               key := &rsa.PrivateKey{}
                                testerr := errors.New("Test Error")
                                var info *mapWorker
                                noWorker := WorkerUUID("There's no such worker")
@@ -352,12 +354,12 @@ var _ = Describe("WorkerList", func() {
                                                return info.State
                                        }).Should(Equal(state))
                                }
-                               eventuallyKey := func(info *mapWorker, key *rsa.PrivateKey) {
+                               eventuallyKey := func(info *mapWorker, match types.GomegaMatcher) {
                                        EventuallyWithOffset(1, func() *rsa.PrivateKey {
                                                wl.mutex.RLock()
                                                defer wl.mutex.RUnlock()
                                                return info.key
-                                       }).Should(Equal(key))
+                                       }).Should(match)
                                }
 
                                BeforeEach(func() {
@@ -390,20 +392,20 @@ var _ = Describe("WorkerList", func() {
                                        It("should work to SetState", func() {
                                                gomock.InOrder(
                                                        dcm.EXPECT().Create(info.dryad),
-                                                       dcm.EXPECT().Prepare().Return(key, nil),
+                                                       dcm.EXPECT().Prepare(gomock.AssignableToTypeOf(&rsa.PublicKey{})).Return(nil),
                                                        dcm.EXPECT().Close(),
                                                )
 
                                                err := wl.SetState(worker, IDLE)
                                                Expect(err).ToNot(HaveOccurred())
                                                eventuallyState(info, IDLE)
-                                               eventuallyKey(info, key)
+                                               eventuallyKey(info, Not(Equal(&rsa.PrivateKey{})))
                                        })
 
                                        It("should fail to SetState if dryadClientManager fails to prepare client", func() {
                                                gomock.InOrder(
                                                        dcm.EXPECT().Create(info.dryad),
-                                                       dcm.EXPECT().Prepare().Return(nil, testerr),
+                                                       dcm.EXPECT().Prepare(gomock.AssignableToTypeOf(&rsa.PublicKey{})).Return(testerr),
                                                        dcm.EXPECT().Close(),
                                                )
 
@@ -793,16 +795,15 @@ var _ = Describe("WorkerList", func() {
                        var ctrl *gomock.Controller
                        var dcm *MockDryadClientManager
                        ip := net.IPv4(2, 4, 6, 8)
-                       key := &rsa.PrivateKey{}
                        testerr := errors.New("Test Error")
                        noWorker := WorkerUUID("There's no such worker")
 
-                       eventuallyKey := func(info *mapWorker, key *rsa.PrivateKey) {
+                       eventuallyKey := func(info *mapWorker, match types.GomegaMatcher) {
                                EventuallyWithOffset(1, func() *rsa.PrivateKey {
                                        wl.mutex.RLock()
                                        defer wl.mutex.RUnlock()
                                        return info.key
-                               }).Should(Equal(key))
+                               }).Should(match)
                        }
                        eventuallyState := func(info *mapWorker, state WorkerState) {
                                EventuallyWithOffset(1, func() WorkerState {
@@ -854,7 +855,7 @@ var _ = Describe("WorkerList", func() {
                                It("should set worker into IDLE state and prepare a key", func() {
                                        gomock.InOrder(
                                                dcm.EXPECT().Create(info.dryad),
-                                               dcm.EXPECT().Prepare().Return(key, nil),
+                                               dcm.EXPECT().Prepare(gomock.AssignableToTypeOf(&rsa.PublicKey{})).Return(nil),
                                                dcm.EXPECT().Close(),
                                        )
 
@@ -862,12 +863,12 @@ var _ = Describe("WorkerList", func() {
                                        Expect(err).NotTo(HaveOccurred())
 
                                        eventuallyState(info, IDLE)
-                                       eventuallyKey(info, key)
+                                       eventuallyKey(info, Not(Equal(&rsa.PrivateKey{})))
                                })
                                It("should fail to prepare worker if dryadClientManager fails to prepare client", func() {
                                        gomock.InOrder(
                                                dcm.EXPECT().Create(info.dryad),
-                                               dcm.EXPECT().Prepare().Return(nil, testerr),
+                                               dcm.EXPECT().Prepare(gomock.AssignableToTypeOf(&rsa.PublicKey{})).Return(testerr),
                                                dcm.EXPECT().Close(),
                                        )
 
@@ -888,6 +889,7 @@ var _ = Describe("WorkerList", func() {
                                })
                        })
                })
+
                Describe("setState with changeListener", func() {
                        var ctrl *gomock.Controller
                        var wc *MockWorkerChange
index da22336..b77a01a 100644 (file)
@@ -18,6 +18,7 @@
 package workers
 
 import (
+       "crypto/rand"
        "crypto/rsa"
        "fmt"
        "math"
@@ -31,6 +32,10 @@ import (
 // UUID denotes a key in Capabilities where WorkerUUID is stored.
 const UUID string = "UUID"
 
+// sizeRSA is a length of the RSA key.
+// It is a variable for test purposes.
+var sizeRSA = 4096
+
 // mapWorker is used by WorkerList to store all
 // (public and private) structures representing Worker.
 type mapWorker struct {
@@ -440,8 +445,7 @@ func (wl *WorkerList) setState(worker WorkerUUID, state WorkerState) error {
        return nil
 }
 
-// prepareKey delegates key generation to Dryad and sets up generated key in the
-// worker. In case of any failure it returns an error.
+// prepareKey generates key, installs public part on worker and stores private part in WorkerList.
 func (wl *WorkerList) prepareKey(worker WorkerUUID) error {
        addr, err := wl.GetWorkerAddr(worker)
        if err != nil {
@@ -453,7 +457,11 @@ func (wl *WorkerList) prepareKey(worker WorkerUUID) error {
                return err
        }
        defer client.Close()
-       key, err := client.Prepare()
+       key, err := rsa.GenerateKey(rand.Reader, sizeRSA)
+       if err != nil {
+               return err
+       }
+       err = client.Prepare(&key.PublicKey)
        if err != nil {
                return err
        }