mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-09 16:00:22 +00:00
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:
parent
2fc10ded65
commit
0c33a33d5c
@ -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
|
||||
|
@ -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
|
||||
|
13
examples/kms/vault/kmip-credentials.yaml
Normal file
13
examples/kms/vault/kmip-credentials.yaml
Normal 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: ""
|
@ -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
20
go.mod
@ -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
47
go.sum
@ -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
527
internal/kms/kmip.go
Normal 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
29
internal/kms/kmip_test.go
Normal 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
43
internal/kms/kms_util.go
Normal 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
|
||||
}
|
88
internal/kms/kms_util_test.go
Normal file
88
internal/kms/kms_util_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user