2021-03-09 22:59:54 +00:00
/ *
Copyright 2021 The Kubernetes Authors All rights reserved .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package addons
import (
"bytes"
"context"
2021-03-17 02:17:29 +00:00
"fmt"
2021-03-09 22:59:54 +00:00
"os"
"os/exec"
"strconv"
2021-03-24 20:47:18 +00:00
"time"
2021-03-09 22:59:54 +00:00
"github.com/pkg/errors"
"golang.org/x/oauth2/google"
2021-03-15 18:39:06 +00:00
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2021-03-19 06:12:05 +00:00
"k8s.io/klog/v2"
2021-03-09 22:59:54 +00:00
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/config"
2021-03-09 23:11:54 +00:00
"k8s.io/minikube/pkg/minikube/detect"
2021-03-09 22:59:54 +00:00
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
2021-03-15 18:39:06 +00:00
"k8s.io/minikube/pkg/minikube/service"
2021-03-09 22:59:54 +00:00
"k8s.io/minikube/pkg/minikube/style"
)
2021-03-09 23:20:02 +00:00
const (
credentialsPath = "/var/lib/minikube/google_application_credentials.json"
projectPath = "/var/lib/minikube/google_cloud_project"
2021-03-17 20:39:55 +00:00
secretName = "gcp-auth"
2021-03-09 23:20:02 +00:00
)
2021-03-09 22:59:54 +00:00
// enableOrDisableGCPAuth enables or disables the gcp-auth addon depending on the val parameter
func enableOrDisableGCPAuth ( cfg * config . ClusterConfig , name string , val string ) error {
enable , err := strconv . ParseBool ( val )
if err != nil {
return errors . Wrapf ( err , "parsing bool: %s" , name )
}
if enable {
return enableAddonGCPAuth ( cfg )
}
return disableAddonGCPAuth ( cfg )
}
func enableAddonGCPAuth ( cfg * config . ClusterConfig ) error {
2021-03-09 23:11:54 +00:00
if ! Force && detect . IsOnGCE ( ) {
2021-03-23 21:41:04 +00:00
exit . Message ( reason . InternalCredsNotNeeded , "It seems that you are running in GCE, which means authentication should work without the GCP Auth addon. If you would still like to authenticate using a credentials file, use the --force flag." )
2021-03-09 22:59:54 +00:00
}
// Grab command runner from running cluster
cc := mustload . Running ( cfg . Name )
r := cc . CP . Runner
// Grab credentials from where GCP would normally look
ctx := context . Background ( )
creds , err := google . FindDefaultCredentials ( ctx )
2021-03-15 18:39:06 +00:00
if err != nil || creds . JSON == nil {
2021-03-09 22:59:54 +00:00
exit . Message ( reason . InternalCredsNotFound , "Could not find any GCP credentials. Either run `gcloud auth application-default login` or set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your credentials file." )
}
2021-03-15 18:39:06 +00:00
// Actually copy the creds over
2021-03-09 22:59:54 +00:00
f := assets . NewMemoryAssetTarget ( creds . JSON , credentialsPath , "0444" )
err = r . Copy ( f )
if err != nil {
return err
}
2021-03-15 18:39:06 +00:00
// Create a registry secret in every namespace we can find
client , err := service . K8s . GetCoreClient ( cfg . Name )
if err != nil {
return err
}
2021-03-17 02:17:29 +00:00
token , err := creds . TokenSource . Token ( )
2021-03-23 21:41:04 +00:00
// Only try to add secret if Token was found
if err == nil {
data := map [ string ] [ ] byte {
2021-03-24 22:09:43 +00:00
".dockercfg" : [ ] byte ( fmt . Sprintf ( ` { "https://gcr.io": { "username":"oauth2accesstoken","password":"%s","email":"none"}, "https://us-docker.pkg.dev": { "username":"oauth2accesstoken","password":"%s","email":"none"}} ` , token . AccessToken , token . AccessToken ) ) ,
2021-03-15 18:39:06 +00:00
}
2021-03-24 20:47:18 +00:00
namespaces , err := client . Namespaces ( ) . List ( context . TODO ( ) , metav1 . ListOptions { } )
2021-03-19 06:12:05 +00:00
if err != nil {
return err
}
2021-03-17 02:17:29 +00:00
2021-03-23 21:41:04 +00:00
for _ , n := range namespaces . Items {
secrets := client . Secrets ( n . Name )
2021-03-15 18:39:06 +00:00
2021-03-24 20:47:18 +00:00
exists := false
secList , err := secrets . List ( context . TODO ( ) , metav1 . ListOptions { } )
2021-03-25 00:11:11 +00:00
if err != nil {
return err
}
2021-03-24 20:47:18 +00:00
for _ , s := range secList . Items {
if s . Name == secretName {
exists = true
2021-03-24 20:52:29 +00:00
break
2021-03-24 20:47:18 +00:00
}
2021-03-23 21:41:04 +00:00
}
2021-03-24 20:47:18 +00:00
if ! exists {
secretObj := & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : secretName ,
} ,
Data : data ,
Type : "kubernetes.io/dockercfg" ,
}
_ , err = secrets . Create ( context . TODO ( ) , secretObj , metav1 . CreateOptions { } )
if err != nil {
return err
}
2021-03-23 21:41:04 +00:00
}
// Now patch the secret into all the service accounts we can find
serviceaccounts := client . ServiceAccounts ( n . Name )
2021-03-24 20:47:18 +00:00
salist , err := serviceaccounts . List ( context . TODO ( ) , metav1 . ListOptions { } )
2021-03-15 18:39:06 +00:00
if err != nil {
return err
}
2021-03-24 20:47:18 +00:00
// Let's make sure we at least find the default service account
for len ( salist . Items ) == 0 {
salist , err = serviceaccounts . List ( context . TODO ( ) , metav1 . ListOptions { } )
if err != nil {
return err
}
time . Sleep ( 1 * time . Second )
}
2021-03-23 21:41:04 +00:00
ips := corev1 . LocalObjectReference { Name : "gcp-auth" }
for _ , sa := range salist . Items {
sa . ImagePullSecrets = append ( sa . ImagePullSecrets , ips )
2021-03-24 20:47:18 +00:00
_ , err := serviceaccounts . Update ( context . TODO ( ) , & sa , metav1 . UpdateOptions { } )
2021-03-23 21:41:04 +00:00
if err != nil {
return err
}
}
}
2021-03-15 18:39:06 +00:00
}
2021-03-09 22:59:54 +00:00
// First check if the project env var is explicitly set
projectEnv := os . Getenv ( "GOOGLE_CLOUD_PROJECT" )
if projectEnv != "" {
f := assets . NewMemoryAssetTarget ( [ ] byte ( projectEnv ) , projectPath , "0444" )
return r . Copy ( f )
}
// We're currently assuming gcloud is installed and in the user's path
project , err := exec . Command ( "gcloud" , "config" , "get-value" , "project" ) . Output ( )
if err == nil && len ( project ) > 0 {
f := assets . NewMemoryAssetTarget ( bytes . TrimSpace ( project ) , projectPath , "0444" )
return r . Copy ( f )
}
out . WarningT ( "Could not determine a Google Cloud project, which might be ok." )
out . Styled ( style . Tip , ` To set your Google Cloud project , run :
gcloud config set project < project name >
or set the GOOGLE_CLOUD_PROJECT environment variable . ` )
// Copy an empty file in to avoid errors about missing files
emptyFile := assets . NewMemoryAssetTarget ( [ ] byte { } , projectPath , "0444" )
return r . Copy ( emptyFile )
}
func disableAddonGCPAuth ( cfg * config . ClusterConfig ) error {
// Grab command runner from running cluster
cc := mustload . Running ( cfg . Name )
r := cc . CP . Runner
// Clean up the files generated when enabling the addon
creds := assets . NewMemoryAssetTarget ( [ ] byte { } , credentialsPath , "0444" )
err := r . Remove ( creds )
if err != nil {
return err
}
project := assets . NewMemoryAssetTarget ( [ ] byte { } , projectPath , "0444" )
err = r . Remove ( project )
if err != nil {
return err
}
2021-03-17 20:39:55 +00:00
client , err := service . K8s . GetCoreClient ( cfg . Name )
if err != nil {
return err
}
2021-03-24 20:47:18 +00:00
namespaces , err := client . Namespaces ( ) . List ( context . TODO ( ) , metav1 . ListOptions { } )
2021-03-17 20:39:55 +00:00
if err != nil {
return err
}
// No need to check for an error here, if the secret doesn't exist, no harm done.
for _ , n := range namespaces . Items {
secrets := client . Secrets ( n . Name )
2021-03-24 20:47:18 +00:00
err := secrets . Delete ( context . TODO ( ) , secretName , metav1 . DeleteOptions { } )
2021-03-19 06:12:05 +00:00
if err != nil {
klog . Infof ( "error deleting secret: %v" , err )
}
2021-03-17 20:39:55 +00:00
}
2021-03-09 22:59:54 +00:00
return nil
}
func verifyGCPAuthAddon ( cc * config . ClusterConfig , name string , val string ) error {
enable , err := strconv . ParseBool ( val )
if err != nil {
return errors . Wrapf ( err , "parsing bool: %s" , name )
}
err = verifyAddonStatusInternal ( cc , name , val , "gcp-auth" )
if enable && err == nil {
out . Styled ( style . Notice , "Your GCP credentials will now be mounted into every pod created in the {{.name}} cluster." , out . V { "name" : cc . Name } )
out . Styled ( style . Notice , "If you don't want your credentials mounted into a specific pod, add a label with the `gcp-auth-skip-secret` key to your pod configuration." )
}
return err
}