rbd: add kmip encryption type

The Key Management Interoperability Protocol (KMIP)
is an extensible communication protocol
that defines message formats for the manipulation
of cryptographic keys on a key management server.
Ceph-CSI can now be configured to connect to
various KMS using KMIP for encrypting RBD volumes.

https://en.wikipedia.org/wiki/Key_Management_Interoperability_Protocol

Signed-off-by: Rakshith R <rar@redhat.com>
This commit is contained in:
Rakshith R 2022-08-16 15:17:05 +05:30 committed by mergify[bot]
parent 2fc10ded65
commit 0c33a33d5c
10 changed files with 803 additions and 13 deletions

View File

@ -415,6 +415,38 @@ the AWS KMS is expected to contain:
This Secret is expected to be created by the tenant/user in each namespace where
Ceph-CSI is used to create encrypted rbd volumes.
#### Configuring KMIP KMS
The Key Management Interoperability Protocol (KMIP) is an extensible
communication protocol that defines message formats for the manipulation
of cryptographic keys on a key management server.
Ceph-CSI can be configured to connect to various KMS servers using
[KMIP](https://en.wikipedia.org/wiki/Key_Management_Interoperability_Protocol)
for encrypting RBD volumes.
There are a few settings that need to be included in the [KMS configuration
file](../examples/kms/vault/kms-config.yaml):
1. `KMS_PROVIDER`: should be set to `kmip`.
1. `KMIP_ENDPOINT` KMIP endpoint address.
1. `KMIP_SECRET_NAME`(optional): name of the Kubernetes Secret which contains
the credentials for communicating with KMIP server, defaults to
`ceph-csi-kmip-credentials`.
1. `TLS_SERVER_NAME`(optional): The endpoint server name. Useful when the
KMIP endpoint does not have a DNS entry.
1. `READ_TIMEOUT`(optional): Network read timeout, in seconds. The default
value is 10.
1. `WRITE_TIMEOUT`(optional): Network write timeout, in seconds. The default
value is 10.
The [Secret with credentials](../examples/kms/vault/kmip-credentials.yaml) for
the KMIP KMS is expected to contain:
1. `CA_CERT`: CA certificate that will be used to connect to KMIP server.
1. `CLIENT_CERT`: Client certificate that will be used to connect to KMIP server.
1. `CLIENT_KEY`: Client key that will be used to connect to KMIP server.
1. `UNIQUE_IDENTIFIER`: Unique ID of the key to use for encrypting/decrypting.
### Encryption prerequisites
In order for encryption to work you need to make sure that `dm-crypt` kernel

View File

@ -74,5 +74,14 @@ data:
"encryptionKMSType": "aws-sts-metadata",
"secretName": "ceph-csi-aws-credentials"
}
kmip-test: |-
{
"KMS_PROVIDER": "kmip",
"KMIP_ENDPOINT": "kmip:5696",
"KMIP_SECRET_NAME": "ceph-csi-kmip-credentials",
"TLS_SERVER_NAME": "kmip.ciphertrustmanager.local",
"READ_TIMEOUT": 10,
"WRITE_TIMEOUT": 10
}
metadata:
name: csi-kms-connection-details

View File

@ -0,0 +1,13 @@
---
# This is an example Kubernetes Secret that can be created in the Kubernetes
# Namespace where Ceph-CSI is deployed. The contents of this Secret will be
# used to connect to the KMS using KMIP.
apiVersion: v1
kind: Secret
metadata:
name: ceph-csi-kmip-credentials
stringData:
CA_CERT: ""
CLIENT_CERT: ""
CLIENT_KEY: ""
UNIQUE_IDENTIFIER: ""

View File

@ -100,6 +100,14 @@ data:
"aws-sts-metadata-test": {
"encryptionKMSType": "aws-sts-metadata",
"secretName": "ceph-csi-aws-credentials"
},
"kmip-test": {
"KMS_PROVIDER": "kmip",
"KMIP_ENDPOINT": "kmip:5696",
"KMIP_SECRET_NAME": "ceph-csi-kmip-credentials",
"TLS_SERVER_NAME": "kmip.ciphertrustmanager.local",
"READ_TIMEOUT": 10,
"WRITE_TIMEOUT": 10
}
}
metadata:

20
go.mod
View File

@ -12,7 +12,9 @@ require (
github.com/container-storage-interface/spec v1.6.0
github.com/csi-addons/replication-lib-utils v0.2.0
github.com/csi-addons/spec v0.1.2-0.20211220115741-32fa508dadbe
github.com/gemalto/kmip-go v0.0.8-0.20220721195433-3fe83e2d3f26
github.com/golang/protobuf v1.5.2
github.com/google/uuid v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/hashicorp/vault/api v1.7.2
@ -25,7 +27,8 @@ require (
github.com/prometheus/client_golang v1.12.2
github.com/stretchr/testify v1.8.0
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886
google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.0
k8s.io/api v0.24.3
@ -42,11 +45,11 @@ require (
sigs.k8s.io/controller-runtime v0.11.0-beta.0.0.20211208212546-f236f0345ad2
)
require golang.org/x/net v0.0.0-20220225172249-27dd8689420f
require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/ansel1/merry v1.6.2 // indirect
github.com/ansel1/merry/v2 v2.0.1 // indirect
github.com/armon/go-metrics v0.3.9 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.16.11 // indirect
@ -65,6 +68,7 @@ require (
github.com/fatih/color v1.9.0 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gemalto/flume v0.13.0 // indirect
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
@ -77,7 +81,6 @@ require (
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@ -104,9 +107,10 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
@ -142,13 +146,15 @@ require (
go.opentelemetry.io/otel/trace v0.20.0 // indirect
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect
google.golang.org/genproto v0.0.0-20220208230804-65c12eb4c068 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect

47
go.sum
View File

@ -115,6 +115,15 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190412020505-60e2075261b6/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/ansel1/merry v1.5.0/go.mod h1:wUy/yW0JX0ix9GYvUbciq+bi3jW/vlKPlbpI7qdZpOw=
github.com/ansel1/merry v1.5.1/go.mod h1:wUy/yW0JX0ix9GYvUbciq+bi3jW/vlKPlbpI7qdZpOw=
github.com/ansel1/merry v1.6.1/go.mod h1:ioJjPJ/IsjxH+cC0lpf5TmbKnbcGa9qTk0fDbeRfnGQ=
github.com/ansel1/merry v1.6.2 h1:0xr40haRrfVzmOH/JVOu7KOKGEI1c/7q5EmgTEbn+Ng=
github.com/ansel1/merry v1.6.2/go.mod h1:pAcMW+2uxIgpzEON021vMtFsrymREY6faJWiiz1QGVQ=
github.com/ansel1/merry/v2 v2.0.0-beta.10/go.mod h1:OUvUYh4KLVhf3+sR9Hk8QxCukijznkpheEd837b7vLg=
github.com/ansel1/merry/v2 v2.0.1 h1:WeiKZdslHPAPFYxTtgX7clC2Vh75NCoWs5OjCZbIA0A=
github.com/ansel1/merry/v2 v2.0.1/go.mod h1:dD5OhpiPrVkvgseRYd+xgYlx7s6ytU3v9BTTJlDA7FM=
github.com/ansel1/vespucci/v4 v4.1.1/go.mod h1:zzdrO4IgBfgcGMbGTk/qNGL8JPslmW3nPpcBHKReFYY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@ -157,6 +166,7 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.16.13/go.mod h1:Ru3QVMLygVs/07UQ3YDu
github.com/aws/smithy-go v1.12.1 h1:yQRC55aXN/y1W10HgwHle01DRuV9Dpf31iGkotjt3Ag=
github.com/aws/smithy-go v1.12.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/baum/kmip-go v0.0.0-20220714190649-7b37ecf92eb2/go.mod h1:5WlKRqL5dfI68V56W+4ZmlPSL+TSfqQrKJYI8CSJz+E=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -351,6 +361,10 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7/go.mod h1:GeIq9qoE43YdGnDXURnmKTnGg15pQz4mYkXSTChbneI=
github.com/gammazero/workerpool v0.0.0-20190406235159-88d534f22b56/go.mod h1:w9RqFVO2BM3xwWEcAB8Fwp0OviTBBEiRmSBDfbXnd3w=
github.com/gemalto/flume v0.13.0 h1:EEeQvAxyFys3BH8IxEU7ZpM6Kr1sYn20HuZq6dgyMR8=
github.com/gemalto/flume v0.13.0/go.mod h1:3iOEZiK/HD8SnFTqHCQoOHQKaHlBY0b6z55P8SLaOzk=
github.com/gemalto/kmip-go v0.0.8-0.20220721195433-3fe83e2d3f26 h1:AGbIx+qTKLkYrrxL6QuwjAR5MvbuX06uMHJFb8mG+ro=
github.com/gemalto/kmip-go v0.0.8-0.20220721195433-3fe83e2d3f26/go.mod h1:7bAnjuzri8yGoJMwngnAd0HdXMRDQU+l1Zaiz12Tr68=
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -358,6 +372,8 @@ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ER
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -740,6 +756,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/keybase/go-crypto v0.0.0-20190403132359-d65b6b94177f h1:Gsc9mVHLRqBjMgdQCghN9NObCcRncDqxJvBvEaIIQEo=
@ -792,21 +810,27 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
github.com/michaelklishin/rabbit-hole v0.0.0-20191008194146-93d9988f0cd5/go.mod h1:+pmbihVqjC3GPdfWv1V2TnRSuVvwrWLKfEP/MZVB/Wc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@ -1179,17 +1203,22 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -1440,6 +1469,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1447,8 +1477,9 @@ golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1544,6 +1575,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=
golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1638,6 +1670,7 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210224155714-063164c882e6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@ -1645,8 +1678,9 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE
google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 h1:Et6SkiuvnBn+SgrSYXs/BrUpGB4mbdwt4R3vaPIlicA=
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220208230804-65c12eb4c068 h1:pwzFiZfBTH/GjBWz1BcDwMBaHBo8mZvpLa7eBKJpFAk=
google.golang.org/genproto v0.0.0-20220208230804-65c12eb4c068/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
@ -1678,6 +1712,7 @@ google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=

527
internal/kms/kmip.go Normal file
View File

@ -0,0 +1,527 @@
/*
Copyright 2022 The Ceph-CSI Authors.
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 kms
import (
"bufio"
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
"time"
"github.com/ceph/ceph-csi/internal/util/k8s"
kmip "github.com/gemalto/kmip-go"
"github.com/gemalto/kmip-go/kmip14"
"github.com/gemalto/kmip-go/ttlv"
"github.com/google/uuid"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
kmsTypeKMIP = "kmip"
// kmipDefaulfReadTimeout is the default read network timeout.
kmipDefaulfReadTimeout = 10
// kmipDefaultWriteTimeout is the default write network timeout.
kmipDefaultWriteTimeout = 10
// KMIP version.
protocolMajor = 1
protocolMinor = 4
// nonceSize is required to generate nonce for encrypting DEK.
nonceSize = 16
// kmipDefaultSecretsName is the default name of the Kubernetes Secret
// that contains the credentials to access the KMIP server. The name of
// the Secret can be configured by setting the `KMIP_SECRET_NAME`
// option.
//
// #nosec:G101, value not credential, just references token.
kmipDefaultSecretsName = "ceph-csi-kmip-credentials"
kmipEndpoint = "KMIP_ENDPOINT"
kmipTLSServerName = "TLS_SERVER_NAME"
kmipReadTimeOut = "READ_TIMEOUT"
kmipWriteTimeOut = "WRITE_TIMEOUT"
// The following options are part of the Kubernetes Secrets.
//
// #nosec:G101, value not credential, just configuration keys.
kmipSecretNameKey = "KMIP_SECRET_NAME"
kmipCACert = "CA_CERT"
kmipCLientCert = "CLIENT_CERT"
kmipClientKey = "CLIENT_KEY"
kmipUniqueIdentifier = "UNIQUE_IDENTIFIER"
)
var _ = RegisterProvider(Provider{
UniqueID: kmsTypeKMIP,
Initializer: initKMIPKMS,
})
type kmipKMS struct {
// basic options to get the secret
secretName string
namespace string
// standard KMIP configuration options
endpoint string
tlsConfig *tls.Config
uniqueIdentifier string
readTimeout uint8
writeTimeout uint8
}
func initKMIPKMS(args ProviderInitArgs) (EncryptionKMS, error) {
kms := &kmipKMS{
namespace: args.Namespace,
}
// get secret name if set, else use default.
err := setConfigString(&kms.secretName, args.Config, kmipSecretNameKey)
if errors.Is(err, errConfigOptionInvalid) {
return nil, err
} else if errors.Is(err, errConfigOptionMissing) {
kms.secretName = kmipDefaultSecretsName
}
err = setConfigString(&kms.endpoint, args.Config, kmipEndpoint)
if err != nil {
return nil, err
}
// optional
serverName := ""
err = setConfigString(&serverName, args.Config, kmipTLSServerName)
if errors.Is(err, errConfigOptionInvalid) {
return nil, err
}
// optional
timeout := kmipDefaulfReadTimeout
err = setConfigInt(&timeout, args.Config, kmipReadTimeOut)
if errors.Is(err, errConfigOptionInvalid) {
return nil, err
}
kms.readTimeout = uint8(timeout)
// optional
timeout = kmipDefaultWriteTimeout
err = setConfigInt(&timeout, args.Config, kmipWriteTimeOut)
if errors.Is(err, errConfigOptionInvalid) {
return nil, err
}
kms.writeTimeout = uint8(timeout)
// read the Kubernetes Secret with CA cert, client cert, client key
// & key unique identifier.
secrets, err := kms.getSecrets()
if err != nil {
return nil, fmt.Errorf("failed to get secrets: %w", err)
}
caCert, found := secrets[kmipCACert]
if !found {
return nil, fmt.Errorf("%w: %s", errConfigOptionMissing, kmipCACert)
}
clientCert, found := secrets[kmipCLientCert]
if !found {
return nil, fmt.Errorf("%w: %s", errConfigOptionMissing, kmipCLientCert)
}
clientKey, found := secrets[kmipClientKey]
if !found {
return nil, fmt.Errorf("%w: %s", errConfigOptionMissing, kmipCLientCert)
}
kms.uniqueIdentifier, found = secrets[kmipUniqueIdentifier]
if !found {
return nil, fmt.Errorf("%w: %s", errConfigOptionMissing, kmipUniqueIdentifier)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(caCert))
cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
if err != nil {
return nil, fmt.Errorf("invalid X509 key pair: %w", err)
}
kms.tlsConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: serverName,
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
}
return kms, nil
}
// EncryptDEK uses the KMIP encrypt operation to encrypt the DEK.
func (kms *kmipKMS) EncryptDEK(_, plainDEK string) (string, error) {
conn, err := kms.connect()
if err != nil {
return "", err
}
defer conn.Close()
emd := encryptedMetedataDEK{}
emd.Nonce, err = generateNonce(nonceSize)
if err != nil {
return "", fmt.Errorf("failed to generated nonce: %w", err)
}
respMsg, decoder, uniqueBatchItemID, err := kms.send(conn,
kmip14.OperationEncrypt,
EncryptRequestPayload{
UniqueIdentifier: kms.uniqueIdentifier,
Data: []byte(plainDEK),
CryptographicParameters: kmip.CryptographicParameters{
PaddingMethod: kmip14.PaddingMethodPKCS5,
CryptographicAlgorithm: kmip14.CryptographicAlgorithmAES,
BlockCipherMode: kmip14.BlockCipherModeCBC,
},
IVCounterNonce: emd.Nonce,
})
if err != nil {
return "", err
}
batchItem, err := kms.verifyResponse(respMsg, kmip14.OperationEncrypt, uniqueBatchItemID)
if err != nil {
return "", err
}
ttlvPayload, ok := batchItem.ResponsePayload.(ttlv.TTLV)
if !ok {
return "", errors.New("failed to parse responsePayload")
}
var encryptRespPayload EncryptResponsePayload
err = decoder.DecodeValue(&encryptRespPayload, ttlvPayload)
if err != nil {
return "", err
}
emd.DEK = encryptRespPayload.Data
emdData, err := json.Marshal(&emd)
if err != nil {
return "", fmt.Errorf("failed to convert "+
"encryptedMetedataDEK to JSON: %w", err)
}
return string(emdData), nil
}
// DecryptDEK uses the KMIP decrypt operation to decrypt the DEK.
func (kms *kmipKMS) DecryptDEK(_, encryptedDEK string) (string, error) {
conn, err := kms.connect()
if err != nil {
return "", err
}
defer conn.Close()
emd := encryptedMetedataDEK{}
err = json.Unmarshal([]byte(encryptedDEK), &emd)
if err != nil {
return "", fmt.Errorf("failed to convert data to "+
"encryptedMetedataDEK: %w", err)
}
respMsg, decoder, uniqueBatchItemID, err := kms.send(conn,
kmip14.OperationDecrypt,
DecryptRequestPayload{
UniqueIdentifier: kms.uniqueIdentifier,
Data: emd.DEK,
IVCounterNonce: emd.DEK,
CryptographicParameters: kmip.CryptographicParameters{
PaddingMethod: kmip14.PaddingMethodPKCS5,
CryptographicAlgorithm: kmip14.CryptographicAlgorithmAES,
BlockCipherMode: kmip14.BlockCipherModeCBC,
},
})
if err != nil {
return "", err
}
batchItem, err := kms.verifyResponse(respMsg, kmip14.OperationDecrypt, uniqueBatchItemID)
if err != nil {
return "", err
}
ttlvPayload, ok := batchItem.ResponsePayload.(ttlv.TTLV)
if !ok {
return "", errors.New("failed to parse responsePayload")
}
var decryptRespPayload DecryptRequestPayload
err = decoder.DecodeValue(&decryptRespPayload, ttlvPayload)
if err != nil {
return "", err
}
return string(decryptRespPayload.Data), nil
}
func (kms *kmipKMS) Destroy() {
// Nothing to do.
}
func (kms *kmipKMS) RequiresDEKStore() DEKStoreType {
return DEKStoreMetadata
}
// getSecrets returns required options from the Kubernetes Secret.
func (kms *kmipKMS) getSecrets() (map[string]string, error) {
c, err := k8s.NewK8sClient()
if err != nil {
return nil, fmt.Errorf("failed to connect to Kubernetes to "+
"get Secret %s/%s: %w", kms.namespace, kms.secretName, err)
}
secret, err := c.CoreV1().Secrets(kms.namespace).Get(context.TODO(),
kms.secretName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get Secret %s/%s: %w",
kms.namespace, kms.secretName, err)
}
config := make(map[string]string)
for k, v := range secret.Data {
switch k {
case kmipClientKey, kmipCLientCert, kmipCACert, kmipUniqueIdentifier:
config[k] = string(v)
default:
return nil, fmt.Errorf("unsupported option for KMS "+
"provider %q: %s", kmsTypeKMIP, k)
}
}
return config, nil
}
// connect to the kmip endpoint, perform TLS and KMIP handshakes.
func (kms *kmipKMS) connect() (*tls.Conn, error) {
conn, err := tls.Dial("tcp", kms.endpoint, kms.tlsConfig)
if err != nil {
return nil, fmt.Errorf("failed to dial kmip connection endpoint: %w", err)
}
if kms.readTimeout != 0 {
err = conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(kms.readTimeout)))
if err != nil {
return nil, fmt.Errorf("failed to set read deadline: %w", err)
}
}
if kms.writeTimeout != 0 {
err = conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(kms.writeTimeout)))
if err != nil {
return nil, fmt.Errorf("failed to set write deadline: %w", err)
}
}
defer func() {
if err != nil {
conn.Close()
}
}()
err = conn.Handshake()
if err != nil {
return nil, fmt.Errorf("failed to perform connection handshake: %w", err)
}
err = kms.discover(conn)
if err != nil {
return nil, err
}
return conn, nil
}
// discover performs KMIP discover operation.
// https://docs.oasis-open.org/kmip/spec/v1.4/kmip-spec-v1.4.html
// chapter 4.26.
func (kms *kmipKMS) discover(conn io.ReadWriter) error {
respMsg, decoder, uniqueBatchItemID, err := kms.send(conn,
kmip14.OperationDiscoverVersions,
kmip.DiscoverVersionsRequestPayload{
ProtocolVersion: []kmip.ProtocolVersion{
{
ProtocolVersionMajor: protocolMajor,
ProtocolVersionMinor: protocolMinor,
},
},
})
if err != nil {
return err
}
batchItem, err := kms.verifyResponse(
respMsg,
kmip14.OperationDiscoverVersions,
uniqueBatchItemID)
if err != nil {
return err
}
ttlvPayload, ok := batchItem.ResponsePayload.(ttlv.TTLV)
if !ok {
return errors.New("failed to parse responsePayload")
}
var respDiscoverVersionsPayload kmip.DiscoverVersionsResponsePayload
err = decoder.DecodeValue(&respDiscoverVersionsPayload, ttlvPayload)
if err != nil {
return err
}
if len(respDiscoverVersionsPayload.ProtocolVersion) != 1 {
return fmt.Errorf("invalid len of discovered protocol versions %v expected 1",
len(respDiscoverVersionsPayload.ProtocolVersion))
}
pv := respDiscoverVersionsPayload.ProtocolVersion[0]
if pv.ProtocolVersionMajor != protocolMajor || pv.ProtocolVersionMinor != protocolMinor {
return fmt.Errorf("invalid discovered protocol version %v.%v expected %v.%v",
pv.ProtocolVersionMajor, pv.ProtocolVersionMinor, protocolMajor, protocolMinor)
}
return nil
}
// send sends KMIP operation over tls connection, returns
// kmip response message,
// ttlv Decoder to decode message into desired format,
// batchItem ID,
// and error.
func (kms *kmipKMS) send(
conn io.ReadWriter,
operation kmip14.Operation,
payload interface{},
) (*kmip.ResponseMessage, *ttlv.Decoder, []byte, error) {
biID := uuid.New()
msg := kmip.RequestMessage{
RequestHeader: kmip.RequestHeader{
ProtocolVersion: kmip.ProtocolVersion{
ProtocolVersionMajor: protocolMajor,
ProtocolVersionMinor: protocolMinor,
},
BatchCount: 1,
},
BatchItem: []kmip.RequestBatchItem{
{
UniqueBatchItemID: biID[:],
Operation: operation,
RequestPayload: payload,
},
},
}
req, err := ttlv.Marshal(msg)
if err != nil {
return nil, nil, nil,
fmt.Errorf("failed to ttlv marshal message: %w", err)
}
_, err = conn.Write(req)
if err != nil {
return nil, nil, nil,
fmt.Errorf("failed to write request onto connection: %w", err)
}
decoder := ttlv.NewDecoder(bufio.NewReader(conn))
resp, err := decoder.NextTTLV()
if err != nil {
return nil, nil, nil,
fmt.Errorf("failed to read ttlv KMIP value: %w", err)
}
var respMsg kmip.ResponseMessage
err = decoder.DecodeValue(&respMsg, resp)
if err != nil {
return nil, nil, nil,
fmt.Errorf("failed to decode response value: %w", err)
}
return &respMsg, decoder, biID[:], nil
}
// verifyResponse verifies the response success and return the batch item.
func (kms *kmipKMS) verifyResponse(
respMsg *kmip.ResponseMessage,
operation kmip14.Operation,
uniqueBatchItemID []byte,
) (*kmip.ResponseBatchItem, error) {
if respMsg.ResponseHeader.BatchCount != 1 {
return nil, fmt.Errorf("batch count %v should be 1", respMsg.ResponseHeader.BatchCount)
}
if len(respMsg.BatchItem) != 1 {
return nil, fmt.Errorf("batch Intems list len %v should be 1",
len(respMsg.BatchItem))
}
batchItem := respMsg.BatchItem[0]
if operation != batchItem.Operation {
return nil, fmt.Errorf("unexpected operation, real %v expected %v",
batchItem.Operation, operation)
}
if !bytes.Equal(uniqueBatchItemID, batchItem.UniqueBatchItemID) {
return nil, fmt.Errorf("unexpected uniqueBatchItemID, real %v expected %v",
batchItem.UniqueBatchItemID, uniqueBatchItemID)
}
if kmip14.ResultStatusSuccess != batchItem.ResultStatus {
return nil, fmt.Errorf("unexpected result status %v expected success %v",
batchItem.ResultStatus, kmip14.ResultStatusSuccess)
}
return &batchItem, nil
}
// TODO: use the following structs from https://github.com/gemalto/kmip-go
// when https://github.com/ThalesGroup/kmip-go/issues/21 is resolved.
// refer: https://docs.oasis-open.org/kmip/spec/v1.4/kmip-spec-v1.4.html.
type EncryptRequestPayload struct {
UniqueIdentifier string
CryptographicParameters kmip.CryptographicParameters
Data []byte
IVCounterNonce []byte
}
type EncryptResponsePayload struct {
UniqueIdentifier string
Data []byte
IVCounterNonce []byte
}
type DecryptRequestPayload struct {
UniqueIdentifier string
CryptographicParameters kmip.CryptographicParameters
Data []byte
IVCounterNonce []byte
}
type DecryptResponsePayload struct {
UniqueIdentifier string
Data []byte
IVCounterNonce []byte
}

29
internal/kms/kmip_test.go Normal file
View File

@ -0,0 +1,29 @@
/*
Copyright 2022 The Ceph-CSI Authors.
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 kms
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestKMIPKMSRegistered(t *testing.T) {
t.Parallel()
_, ok := kmsManager.providers[kmsTypeKMIP]
assert.True(t, ok)
}

43
internal/kms/kms_util.go Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright 2022 The Ceph-CSI Authors.
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 kms
import "fmt"
// setConfigInt fetches a value from a configuration map and converts it to
// a integer.
//
// If the value is not available, *option is not adjusted and
// errConfigOptionMissing is returned.
// In case the value is available, but can not be converted to a string,
// errConfigOptionInvalid is returned.
func setConfigInt(option *int, config map[string]interface{}, key string) error {
value, ok := config[key]
if !ok {
return fmt.Errorf("%w: %s", errConfigOptionMissing, key)
}
s, ok := value.(float64)
if !ok {
return fmt.Errorf("%w: expected float64 for %q, but got %T",
errConfigOptionInvalid, key, value)
}
*option = int(s)
return nil
}

View File

@ -0,0 +1,88 @@
/*
Copyright 2022 The Ceph-CSI Authors.
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 kms
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestSetConfigInt(t *testing.T) {
t.Parallel()
type args struct {
option *int
config map[string]interface{}
key string
}
option := 1
tests := []struct {
name string
args args
err error
value int
}{
{
name: "valid value",
args: args{
option: &option,
config: map[string]interface{}{
"a": 1.0,
},
key: "a",
},
err: nil,
value: 1,
},
{
name: "invalid value",
args: args{
option: &option,
config: map[string]interface{}{
"a": "abc",
},
key: "a",
},
err: errConfigOptionInvalid,
value: 0,
},
{
name: "missing value",
args: args{
option: &option,
config: map[string]interface{}{},
key: "a",
},
err: errConfigOptionMissing,
value: 0,
},
}
for _, tt := range tests {
currentTT := tt
t.Run(currentTT.name, func(t *testing.T) {
t.Parallel()
err := setConfigInt(currentTT.args.option, currentTT.args.config, currentTT.args.key)
if !errors.Is(err, currentTT.err) {
t.Errorf("setConfigInt() error = %v, wantErr %v", err, currentTT.err)
}
if err != nil {
assert.NotEqual(t, currentTT.value, currentTT.args.option)
}
})
}
}