-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for TLS #692
base: main
Are you sure you want to change the base?
Add support for TLS #692
Conversation
b873181
to
b0c9bcd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left a few minor comments for improving, looks pretty good to me already. Thanks!
@nixpanic should we disable it by default for easy opt-in for users? |
If it works on current Kubernetes installations by default, it can be enabled by default IMHO. You may want to add a note in https://github.com/csi-addons/kubernetes-csi-addons/blob/main/docs/deploy-controller.md on the requirements of Kubernetes services, and how to disable it in case those services are unavailable. |
@Madhu-1 my PR fails until I revert code introduced at https://github.com/csi-addons/kubernetes-csi-addons/pull/695/files#diff-03e0410a78ce106bc8ee10f594661c87995994955bfb6b07972d8fde2ec8a555R125-R146 |
@bipuladh please use latest yamls from master branch in Rook |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a documentation about this feature and as its enabled by default i assume upgraded works as it is (in kubernetes)
internal/connection/connection.go
Outdated
} | ||
tlsConfig := &tls.Config{ | ||
RootCAs: caCertPool, // The CA certificates to verify the server | ||
InsecureSkipVerify: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont we need to set it to true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default setting it to false but making it configurable by users from args
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After having discussions we need the ability to configure this to support certificates. We currently resolve the IP address and use that instead of host name. This causes certificate mismatch.
internal/kubernetes/token/grpc.go
Outdated
return status.Errorf(codes.Unauthenticated, "missing metadata") | ||
} | ||
|
||
authHeader, ok := md["authorization"] | ||
if !ok || len(authHeader) == 0 { | ||
return status.Errorf(codes.Unauthenticated, "missing authorization token") | ||
} | ||
|
||
token := authHeader[0] | ||
isValidated, err := validateBearerToken(ctx, token, kubeclient) | ||
if !isValidated || (err != nil) { | ||
return status.Errorf(codes.Unauthenticated, "invalid token") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think you can use status.Error instead of status.Errorf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return the err message as well for better debugging
internal/kubernetes/token/grpc.go
Outdated
file, err := os.Open(tokenPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer file.Close() | ||
|
||
data, err := io.ReadAll(file) | ||
if err != nil { | ||
return "", err | ||
} | ||
return string(data), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make use of readfile function which is doing the same thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ioutil readFile is deprecated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i meant readFile
we have below in this file
sidecar/internal/server/server.go
Outdated
ss.server = grpc.NewServer(grpc.UnaryInterceptor(token.AuthorizationInterceptor(ss.client)), grpc.Creds(creds)) | ||
} | ||
if !ss.enableTLS { | ||
ss.server = grpc.NewServer(grpc.UnaryInterceptor(token.AuthorizationInterceptor(ss.client))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if tls is not enabled why we need to have add UnaryInterceptor
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is something we need to decide. Should we disable auth token verification if TLS is disabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing Interceptor when TLS not used.
internal/connection/connection.go
Outdated
} | ||
tlsConfig := &tls.Config{ | ||
RootCAs: caCertPool, // The CA certificates to verify the server | ||
InsecureSkipVerify: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be set to false
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default setting it to false but making it configurable by users from args
internal/kubernetes/token/grpc.go
Outdated
return nil | ||
} | ||
|
||
func removeBearer(authHeader string) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func removeBearer(authHeader string) string { | |
func extractToken(authHeader string) string { |
since the function just extracts the token, this naming is more suitable?
@Madhu-1 @nixpanic for a smoother upgrade experience, should we disable by default? As mentioned in the PR description the sidecar deployer will have to create a certificate and mount it if not done the sidecar would go to CLBO. I can add this setup procedure on the readme files for reference. |
Yes, users should not need to do anything extra for upgrading. If there are manual steps to configure certificates, disable the feature by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Please squash all the commits together so that the Mergify automation can merge it when all approvals are in.
internal/kubernetes/token/grpc.go
Outdated
} | ||
|
||
token := authHeader[0] | ||
isValidated, err := validateBearerToken(ctx, token, kubeclient) | ||
if !isValidated || (err != nil) { | ||
return status.Errorf(codes.Unauthenticated, "invalid token") | ||
return status.Error(codes.Unauthenticated, fmt.Sprint("invalid token: %w", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is where status.Errorf()
can be used so that fmt.Sprintf()
is not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please address this as well?
cmd/manager/main.go
Outdated
@@ -92,6 +94,8 @@ func main() { | |||
flag.BoolVar(&enableAdmissionWebhooks, "enable-admission-webhooks", false, "[DEPRECATED] Enable the admission webhooks") | |||
flag.BoolVar(&showVersion, "version", false, "Print Version details") | |||
flag.StringVar(&cfg.SchedulePrecedence, "schedule-precedence", "", "The order of precedence in which schedule of reclaimspace and keyrotation is considered. Possible values are sc-only") | |||
flag.BoolVar(&enableTLS, "enable-tls", false, "Enable TLS(enabled by default)") | |||
flag.BoolVar(&skipInsecureVerify, "insecure-skip-tls-verify", false, "skip the certificate check") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flag.BoolVar(&skipInsecureVerify, "insecure-skip-tls-verify", false, "skip the certificate check") | |
flag.BoolVar(&skipInsecureVerify, "insecure-skip-tls-verify", false, "skip server certificate verification") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add/update the documentation on how to enable this functionality
cmd/manager/main.go
Outdated
@@ -92,6 +94,8 @@ func main() { | |||
flag.BoolVar(&enableAdmissionWebhooks, "enable-admission-webhooks", false, "[DEPRECATED] Enable the admission webhooks") | |||
flag.BoolVar(&showVersion, "version", false, "Print Version details") | |||
flag.StringVar(&cfg.SchedulePrecedence, "schedule-precedence", "", "The order of precedence in which schedule of reclaimspace and keyrotation is considered. Possible values are sc-only") | |||
flag.BoolVar(&enableTLS, "enable-tls", false, "Enable TLS(enabled by default)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to update the comment as its disabled by default
internal/connection/connection.go
Outdated
grpc.WithIdleTimeout(time.Duration(0)), | ||
} | ||
|
||
opts = append(opts, token.WithServiceAccountToken()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to move this inside the if check when TLS is enabled?
internal/connection/connection.go
Outdated
|
||
caFile, caError := token.GetCACert() | ||
if caError != nil { | ||
return nil, (fmt.Errorf("failed to get server cert %w", caError)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return nil, (fmt.Errorf("failed to get server cert %w", caError)) | |
return nil, fmt.Errorf("failed to get server cert %w", caError) |
internal/kubernetes/token/grpc.go
Outdated
file, err := os.Open(tokenPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer file.Close() | ||
|
||
data, err := io.ReadAll(file) | ||
if err != nil { | ||
return "", err | ||
} | ||
return string(data), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i meant readFile
we have below in this file
internal/kubernetes/token/grpc.go
Outdated
func parseToken(authHeader string) string { | ||
if strings.HasPrefix(authHeader, bearerPrefix) { | ||
return strings.TrimPrefix(authHeader, bearerPrefix) | ||
} | ||
return authHeader | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the goal is to just to parse, we can remove the if check and trim it
func parseToken(authHeader string) string {
return strings.TrimPrefix(authHeader, bearerPrefix)
}
result, err := kubeclient.AuthenticationV1().TokenReviews().Create(ctx, tokenReview, metav1.CreateOptions{}) | ||
if err != nil { | ||
return false, fmt.Errorf("failed to review token %w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dont we need RBAC for this one? am not sure we have this RBAC already
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we require. Whoever deploys the sidecar should be responsible to have this.
sidecar/internal/server/server.go
Outdated
// create the gRPC server and register services | ||
ss.server = grpc.NewServer() | ||
if ss.enableTLS { | ||
creds, err := credentials.NewServerTLSFromFile("/etc/tls/tls.crt", "/etc/tls/tls.key") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"/etc/tls/tls.crt", "/etc/tls/tls.key"
Are we going to have these two files in all the pods in same location when TLS is enabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally yes. But I guess we can make it configurable later on if that is preferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding these as parameters
sidecar/internal/server/server.go
Outdated
if ss.enableTLS { | ||
creds, err := credentials.NewServerTLSFromFile("/etc/tls/tls.crt", "/etc/tls/tls.key") | ||
if err != nil { | ||
klog.Fatalf("Could not find TLS file: %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
klog.Fatalf("Could not find TLS file: %v", err) | |
klog.Fatalf("failed to load TLS certificate and key: %v", err) |
90f849e
to
157b439
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty complete to me, thanks for the contribution!
Pull request has been modified.
Pull request has been modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some nits, overall LGTM
opts := []grpc.DialOption{ | ||
grpc.WithTransportCredentials(insecure.NewCredentials()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dont we need to set this incase enableTLS is set to false?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bipuladh wont this be a problem when enableTLS is set we are appending DialOptions with two grpc.WithTransportCredentials
? one with insecure and another one with creds?
@@ -211,6 +213,10 @@ func (r *CSIAddonsNodeReconciler) resolveEndpoint(ctx context.Context, rawURL st | |||
if err != nil { | |||
return "", "", err | |||
} | |||
if r.EnableTLS { | |||
// We need to use this name to accept certificates signed for pods | |||
return podname, podname + "." + namespace + ".pod" + ":" + port, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this going to work when we are using host networking?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing this.
internal/kubernetes/token/grpc.go
Outdated
} | ||
|
||
token := authHeader[0] | ||
isValidated, err := validateBearerToken(ctx, token, kubeclient) | ||
if !isValidated || (err != nil) { | ||
return status.Errorf(codes.Unauthenticated, "invalid token") | ||
return status.Error(codes.Unauthenticated, fmt.Sprint("invalid token: %w", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please address this as well?
"net" | ||
|
||
"github.com/csi-addons/kubernetes-csi-addons/internal/kubernetes/token" | ||
"google.golang.org/grpc" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a new line for separation of imports
@bipuladh i tested this PR on the existing cluster without enabling TLS. its not working, can you please ensure that the functionality works with/without TLS? |
bdc2e10
to
86bd3b3
Compare
Tested with the newly updated code. Working fine now. |
/hold |
Signed-off-by: Bipul Adhikari <[email protected]>
} | ||
|
||
func GetCACert() (string, error) { | ||
return readFile("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This path is something that should be configurable. Users may want to provide their own certificates for the sidecar, and those need to be verified with a CA cert that trusts the authority that was used to sign the sidecar certificates.
Changes
Requirements for the above features to work correctly:
Images for testing:
Test logs
2024-11-11T18:12:00.948Z INFO Successfully connected to sidecar {"controller": "csiaddonsnode", "controllerGroup": "csiaddons.openshift.io", "controllerKind": "CSIAddonsNode", "CSIAddonsNode": {"name":"csi-rbdplugin-gbr85","namespace":"openshift-storage"}, "namespace": "openshift-storage", "name": "csi-rbdplugin-gbr85", "reconcileID": "964eea1e-324b-4957-984d-2611972645ed", "NodeID": "ip-10-0-50-177.us-east-2.compute.internal", "DriverName": "openshift-storage.rbd.csi.ceph.com", "EndPoint": "10.0.50.177:9070"}