Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
name = "google.golang.org/grpc"
version = "1.9.2"

[[constraint]]
name = "gopkg.in/yaml.v2"
version = "v2.1.1"

[prune]
go-tests = true
unused-packages = true
24 changes: 24 additions & 0 deletions cmd/csi-sanity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ For verbose type:
$ csi-sanity --ginkgo.v --csi.endpoint=<your csi driver endpoint>
```

For csi-credentials, create a secrets file with all the secrets in it:
```yaml
CreateVolumeSecret:
secretKey: secretval1
DeleteVolumeSecret:
secretKey: secretval2
ControllerPublishVolumeSecret:
secretKey: secretval3
ControllerUnpublishVolumeSecret:
secretKey: secretval4
NodeStageVolumeSecret:
secretKey: secretval5
NodePublishVolumeSecret:
secretKey: secretval6
```

Pass the file path to csi-sanity as:
```
$ csi-sanity --csi.endpoint=<your csi driver endpoint> --csi.secrets=<path to secrets file>
```

Replace the keys and values of the credentials appropriately. Since the whole
secret is passed in the request, multiple key-val pairs can be used.

### Help
The full Ginkgo and golang unit test parameters are available. Type

Expand Down
1 change: 1 addition & 0 deletions cmd/csi-sanity/sanity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func init() {
flag.BoolVar(&version, prefix+"version", false, "Version of this program")
flag.StringVar(&config.TargetPath, prefix+"mountdir", os.TempDir()+"/csi", "Mount point for NodePublish")
flag.StringVar(&config.StagingPath, prefix+"stagingdir", os.TempDir()+"/csi", "Mount point for NodeStage if staging is supported")
flag.StringVar(&config.SecretsFile, prefix+"secrets", "", "CSI secrets file")
flag.Parse()
}

Expand Down
116 changes: 115 additions & 1 deletion driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,55 @@ limitations under the License.
package driver

import (
context "context"
"errors"
"net"
"sync"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

csi "github.com/container-storage-interface/spec/lib/go/csi/v0"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

var (
// ErrNoCredentials is the error when a secret is enabled but not passed in the request.
ErrNoCredentials = errors.New("secret must be provided")
// ErrAuthFailed is the error when the secret is incorrect.
ErrAuthFailed = errors.New("authentication failed")
)

type CSIDriverServers struct {
Controller csi.ControllerServer
Identity csi.IdentityServer
Node csi.NodeServer
}

// This is the key name in all the CSI secret objects.
const secretField = "secretKey"

// CSICreds is a driver specific secret type. Drivers can have a key-val pair of
// secrets. This mock driver has a single string secret with secretField as the
// key.
type CSICreds struct {
CreateVolumeSecret string
DeleteVolumeSecret string
ControllerPublishVolumeSecret string
ControllerUnpublishVolumeSecret string
NodeStageVolumeSecret string
NodePublishVolumeSecret string
}

type CSIDriver struct {
listener net.Listener
server *grpc.Server
servers *CSIDriverServers
wg sync.WaitGroup
running bool
lock sync.Mutex
creds *CSICreds
}

func NewCSIDriver(servers *CSIDriverServers) *CSIDriver {
Expand Down Expand Up @@ -71,7 +99,9 @@ func (c *CSIDriver) Start(l net.Listener) error {
c.listener = l

// Create a new grpc server
c.server = grpc.NewServer()
c.server = grpc.NewServer(
grpc.UnaryInterceptor(c.authInterceptor),
)

// Register Mock servers
if c.servers.Controller != nil {
Expand Down Expand Up @@ -115,3 +145,87 @@ func (c *CSIDriver) IsRunning() bool {

return c.running
}

// SetDefaultCreds sets the default secrets for CSI creds.
func (c *CSIDriver) SetDefaultCreds() {
c.creds = &CSICreds{
CreateVolumeSecret: "secretval1",
DeleteVolumeSecret: "secretval2",
ControllerPublishVolumeSecret: "secretval3",
ControllerUnpublishVolumeSecret: "secretval4",
NodeStageVolumeSecret: "secretval5",
NodePublishVolumeSecret: "secretval6",
}
}

func (c *CSIDriver) authInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if c.creds != nil {
authenticated, authErr := isAuthenticated(req, c.creds)
if !authenticated {
if authErr == ErrNoCredentials {
return nil, status.Error(codes.InvalidArgument, authErr.Error())
}
if authErr == ErrAuthFailed {
return nil, status.Error(codes.Unauthenticated, authErr.Error())
}
}
}

h, err := handler(ctx, req)

return h, err
}

func isAuthenticated(req interface{}, creds *CSICreds) (bool, error) {
switch r := req.(type) {
case *csi.CreateVolumeRequest:
return authenticateCreateVolume(r, creds)
case *csi.DeleteVolumeRequest:
return authenticateDeleteVolume(r, creds)
case *csi.ControllerPublishVolumeRequest:
return authenticateControllerPublishVolume(r, creds)
case *csi.ControllerUnpublishVolumeRequest:
return authenticateControllerUnpublishVolume(r, creds)
case *csi.NodeStageVolumeRequest:
return authenticateNodeStageVolume(r, creds)
case *csi.NodePublishVolumeRequest:
return authenticateNodePublishVolume(r, creds)
default:
return true, nil
}
}

func authenticateCreateVolume(req *csi.CreateVolumeRequest, creds *CSICreds) (bool, error) {
return credsCheck(req.GetControllerCreateSecrets(), creds.CreateVolumeSecret)
}

func authenticateDeleteVolume(req *csi.DeleteVolumeRequest, creds *CSICreds) (bool, error) {
return credsCheck(req.GetControllerDeleteSecrets(), creds.DeleteVolumeSecret)
}

func authenticateControllerPublishVolume(req *csi.ControllerPublishVolumeRequest, creds *CSICreds) (bool, error) {
return credsCheck(req.GetControllerPublishSecrets(), creds.ControllerPublishVolumeSecret)
}

func authenticateControllerUnpublishVolume(req *csi.ControllerUnpublishVolumeRequest, creds *CSICreds) (bool, error) {
return credsCheck(req.GetControllerUnpublishSecrets(), creds.ControllerUnpublishVolumeSecret)
}

func authenticateNodeStageVolume(req *csi.NodeStageVolumeRequest, creds *CSICreds) (bool, error) {
return credsCheck(req.GetNodeStageSecrets(), creds.NodeStageVolumeSecret)
}

func authenticateNodePublishVolume(req *csi.NodePublishVolumeRequest, creds *CSICreds) (bool, error) {
return credsCheck(req.GetNodePublishSecrets(), creds.NodePublishVolumeSecret)
}

func credsCheck(secrets map[string]string, secretVal string) (bool, error) {
if len(secrets) == 0 {
return false, ErrNoCredentials
}

if secrets[secretField] != secretVal {
return false, ErrAuthFailed
}
return true, nil
}
16 changes: 16 additions & 0 deletions hack/e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ runTest()
fi
}

runTestWithCreds()
{
CSI_ENDPOINT=$1 CSI_ENABLE_CREDS=true mock &
local pid=$!

csi-sanity $TESTARGS --csi.endpoint=$2 --csi.secrets=mock/mocksecret.yaml; ret=$?
kill -9 $pid

if [ $ret -ne 0 ] ; then
exit $ret
fi
}

go install ./mock || exit 1

cd cmd/csi-sanity
Expand All @@ -31,4 +44,7 @@ cd ../..
runTest "${UDS}" "${UDS}"
rm -f $UDS

runTestWithCreds "${UDS}" "${UDS}"
rm -f $UDS

exit 0
6 changes: 6 additions & 0 deletions mock/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ func main() {
}
d := driver.NewCSIDriver(servers)

// If creds is enabled, set the default creds.
setCreds := os.Getenv("CSI_ENABLE_CREDS")
if len(setCreds) > 0 && setCreds == "true" {
d.SetDefaultCreds()
}

// Listen
os.Remove(endpoint)
l, err := net.Listen("unix", endpoint)
Expand Down
12 changes: 12 additions & 0 deletions mock/mocksecret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CreateVolumeSecret:
secretKey: secretval1
DeleteVolumeSecret:
secretKey: secretval2
ControllerPublishVolumeSecret:
secretKey: secretval3
ControllerUnpublishVolumeSecret:
secretKey: secretval4
NodeStageVolumeSecret:
secretKey: secretval5
NodePublishVolumeSecret:
secretKey: secretval6
Loading