diff --git a/cli/cmd/configure_domain.go b/cli/cmd/configure_domain.go index 99b596b9aa..4fec888678 100644 --- a/cli/cmd/configure_domain.go +++ b/cli/cmd/configure_domain.go @@ -223,20 +223,26 @@ func updateKeptnAPIVirtualService(path, domain string) error { func updateCertificate(path, domain string) error { - template := &x509.Certificate{ - IsCA: true, - BasicConstraintsValid: true, - SubjectKeyId: []byte(domain), - SerialNumber: big.NewInt(1234), + // Source: https://golang.org/src/crypto/tls/generate_cert.go + // We can verify the generated key with 'openssl rsa -in key.pem -check' + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, Subject: pkix.Name{ - Country: []string{"Austria"}, - Organization: []string{"keptn"}, + Organization: []string{"Keptn"}, }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(1, 0, 0), - // see http://golang.org/pkg/crypto/x509/#KeyUsage - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{domain}, } // generate private key @@ -247,31 +253,38 @@ func updateCertificate(path, domain string) error { publickey := &privatekey.PublicKey - // create a self-signed certificate. template = parent - var parent = template - cert, err := x509.CreateCertificate(rand.Reader, template, parent, publickey, privatekey) + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publickey, privatekey) if err != nil { return err } privateKeyPath := path + "private.key" - keyfile, _ := os.Create(privateKeyPath) - var pemkey = &pem.Block{ - Type: "PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(privatekey)} - pem.Encode(keyfile, pemkey) - keyfile.Close() - defer os.Remove(privateKeyPath) - certPath := path + "cert.pem" - pemfile, _ := os.Create(certPath) - var pemCert = &pem.Block{ - Type: "CERTIFICATE", - Bytes: cert} - pem.Encode(pemfile, pemCert) - pemfile.Close() + + certOut, err := os.Create(certPath) + if err != nil { + return err + } + if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { + return err + } + if err := certOut.Close(); err != nil { + return err + } defer os.Remove(certPath) + keyOut, err := os.OpenFile(privateKeyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + if err := pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privatekey)}); err != nil { + return err + } + if err := keyOut.Close(); err != nil { + return err + } + defer os.Remove(privateKeyPath) + // First delete secret o := options{"delete", "--namespace", "istio-system", "secret", "istio-ingressgateway-certs"} o.appendIfNotEmpty(kubectlOptions) diff --git a/cli/cmd/install.go b/cli/cmd/install.go index ba19d5fc95..42b708a893 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -39,6 +39,7 @@ var platformIdentifier *string const gke = "gke" const aks = "aks" +const eks = "eks" const openshift = "openshift" const kubernetes = "kubernetes" @@ -106,11 +107,20 @@ Please see https://kubernetes.io/docs/tasks/tools/install-kubectl/`) if err != nil { return err } - // Verify the provided config - // Check whether all data is provided - if p.getGithubCreds().GithubPersonalAccessToken == "" || - p.getGithubCreds().GithubOrg == "" || p.getGithubCreds().GithubUserName == "" { - return errors.New("Incomplete credential file " + *configFilePath) + + _, eks := p.(*eksPlatform) + if !eks { + // Verify the provided config + // Check whether all data is provided + if p.getGithubCreds().GithubPersonalAccessToken == "" || + p.getGithubCreds().GithubOrg == "" || p.getGithubCreds().GithubUserName == "" { + return errors.New("Incomplete credential file " + *configFilePath) + } + + err = checkGithubCreds() + if err != nil { + return err + } } // Check whether the authentication at the cluster is valid @@ -119,10 +129,6 @@ Please see https://kubernetes.io/docs/tasks/tools/install-kubectl/`) return err } - err = checkGithubCreds() - if err != nil { - return err - } } return nil @@ -176,6 +182,9 @@ func setPlatform() error { case aks: p = newAKSPlatform() return nil + case eks: + p = newEKSPlatform() + return nil case openshift: p = newOpenShiftPlatform() return nil @@ -183,7 +192,7 @@ func setPlatform() error { p = newKubernetesPlatform() return nil default: - return errors.New("Unsupported platform '" + *platformIdentifier + "'. The following platforms are supported: gke, aks, openshift, and kubernetes") + return errors.New("Unsupported platform '" + *platformIdentifier + "'. The following platforms are supported: aks, eks, gke, openshift, and kubernetes") } } @@ -193,7 +202,7 @@ func init() { installCmd.Flags().MarkHidden("creds") installerVersion = installCmd.Flags().StringP("keptn-version", "k", "master", "The branch or tag of the version which is installed") installCmd.Flags().MarkHidden("keptn-version") - platformIdentifier = installCmd.Flags().StringP("platform", "p", "gke", "The platform to run keptn on [gke,openshift,aks,kubernetes]") + platformIdentifier = installCmd.Flags().StringP("platform", "p", "gke", "The platform to run keptn on [aks,eks,gke,openshift,kubernetes]") installCmd.PersistentFlags().BoolVarP(&insecureSkipTLSVerify, "insecure-skip-tls-verify", "s", false, "Skip tls verification for kubectl commands") } @@ -244,10 +253,11 @@ func doInstallation() error { return err } - _, gke := p.(*gkePlatform) _, aks := p.(*aksPlatform) + _, eks := p.(*eksPlatform) + _, gke := p.(*gkePlatform) _, k8s := p.(*kubernetesPlatform) - if gke || aks || k8s { + if gke || aks || k8s || eks { options := options{"apply", "-f", getRbacURL()} options.appendIfNotEmpty(kubectlOptions) _, err = keptnutils.ExecuteCommand("kubectl", options) @@ -259,9 +269,9 @@ func doInstallation() error { utils.PrintLog("Deploying keptn installer pod...", utils.InfoLevel) - options := options{"apply", "-f", installerPath} - options.appendIfNotEmpty(kubectlOptions) - _, err = keptnutils.ExecuteCommand("kubectl", options) + o := options{"apply", "-f", installerPath} + o.appendIfNotEmpty(kubectlOptions) + _, err = keptnutils.ExecuteCommand("kubectl", o) if err != nil { return fmt.Errorf("Error while deploying keptn installer pod: %s \nAborting installation", err.Error()) @@ -279,17 +289,29 @@ func doInstallation() error { return err } - // installation finished, get auth token and endpoint - err = authUsingKube() - if err != nil { - return err - } - err = configure(p.getGithubCreds().GithubOrg, - p.getGithubCreds().GithubUserName, p.getGithubCreds().GithubPersonalAccessToken) - if err != nil { - return err - } + if eks { + o = options{"get", "svc", "istio-ingressgateway", "-n", "istio-system", + "-ojsonpath={.status.loadBalancer.ingress[0].hostname}"} + o.appendIfNotEmpty(kubectlOptions) + hostname, err := keptnutils.ExecuteCommand("kubectl", o) + if err != nil { + return err + } + fmt.Println("Please create a Route53 Hosted Zone with a wildcard record set for " + hostname) + fmt.Println("Afterwards, call 'keptn configure domain YOUR_ROUTE53_DOMAIN'") + } else { + // installation finished, get auth token and endpoint + err = authUsingKube() + if err != nil { + return err + } + err = configure(p.getGithubCreds().GithubOrg, + p.getGithubCreds().GithubUserName, p.getGithubCreds().GithubPersonalAccessToken) + if err != nil { + return err + } + } return os.Remove(installerPath) } @@ -312,34 +334,38 @@ func readCreds() error { fmt.Print("Please enter the following information or press enter to keep the old value:\n") + _, eks := p.(*eksPlatform) + for { p.readCreds() - readGithubUserName(p.getGithubCreds()) + if !eks { + readGithubUserName(p.getGithubCreds()) - // Check if the access token has the necessary permissions and the github org exists - validScopeRes := false - for !validScopeRes { - readGithubPersonalAccessToken(p.getGithubCreds()) - validScopeRes, err = utils.HasTokenRepoScope(p.getGithubCreds().GithubPersonalAccessToken) - if err != nil { - return err - } - if !validScopeRes { - fmt.Println("GitHub Personal Access Token requies at least a 'repo'-scope") - p.getGithubCreds().GithubPersonalAccessToken = "" - } - } - validOrg := false - for !validOrg { - readGithubOrg(p.getGithubCreds()) - validOrg, err = utils.IsOrgExisting(p.getGithubCreds().GithubPersonalAccessToken, p.getGithubCreds().GithubOrg) - if err != nil { - return err + // Check if the access token has the necessary permissions and the github org exists + validScopeRes := false + for !validScopeRes { + readGithubPersonalAccessToken(p.getGithubCreds()) + validScopeRes, err = utils.HasTokenRepoScope(p.getGithubCreds().GithubPersonalAccessToken) + if err != nil { + return err + } + if !validScopeRes { + fmt.Println("GitHub Personal Access Token requies at least a 'repo'-scope") + p.getGithubCreds().GithubPersonalAccessToken = "" + } } - if !validOrg { - fmt.Println("Provided GitHub Organization " + p.getGithubCreds().GithubOrg + " does not exist.") - p.getGithubCreds().GithubOrg = "" + validOrg := false + for !validOrg { + readGithubOrg(p.getGithubCreds()) + validOrg, err = utils.IsOrgExisting(p.getGithubCreds().GithubPersonalAccessToken, p.getGithubCreds().GithubOrg) + if err != nil { + return err + } + if !validOrg { + fmt.Println("Provided GitHub Organization " + p.getGithubCreds().GithubOrg + " does not exist.") + p.getGithubCreds().GithubOrg = "" + } } } @@ -348,9 +374,11 @@ func readCreds() error { p.printCreds() - fmt.Println("GitHub User Name: " + p.getGithubCreds().GithubUserName) - fmt.Println("GitHub Personal Access Token: " + p.getGithubCreds().GithubPersonalAccessToken) - fmt.Println("GitHub Organization: " + p.getGithubCreds().GithubOrg) + if !eks { + fmt.Println("GitHub User Name: " + p.getGithubCreds().GithubUserName) + fmt.Println("GitHub Personal Access Token: " + p.getGithubCreds().GithubPersonalAccessToken) + fmt.Println("GitHub Organization: " + p.getGithubCreds().GithubOrg) + } fmt.Println() fmt.Println("Is this all correct? (y/n)") diff --git a/cli/cmd/installAKS.go b/cli/cmd/installAKS.go index 7747d06326..18ed566e19 100644 --- a/cli/cmd/installAKS.go +++ b/cli/cmd/installAKS.go @@ -54,7 +54,7 @@ func (p aksPlatform) checkRequirements() error { func (p aksPlatform) checkCreds() error { if p.creds.ClusterName == "" || p.creds.AzureResourceGroup == "" || p.creds.AzureSubscription == "" { - return errors.New("Incomplete credential file " + *configFilePath) + return errors.New("Incomplete credentials") } authenticated, err := p.authenticateAtCluster() diff --git a/cli/cmd/installEKS.go b/cli/cmd/installEKS.go new file mode 100644 index 0000000000..f6466532e3 --- /dev/null +++ b/cli/cmd/installEKS.go @@ -0,0 +1,200 @@ +// Copyright © 2019 NAME HERE +// +// 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 cmd + +import ( + "encoding/json" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + + keptnutils "github.com/keptn/go-utils/pkg/utils" +) + +type eksCredentials struct { + ClusterName string `json:"clusterName"` + AwsRegion string `json:"awsRegion"` +} + +type eksPlatform struct { + creds *eksCredentials +} + +func newEKSPlatform() *eksPlatform { + return &eksPlatform{ + creds: &eksCredentials{}, + } +} + +func (p eksPlatform) getGithubCreds() *githubCredentials { + return nil +} + +func (p eksPlatform) getCreds() interface{} { + return p.creds +} + +func (p eksPlatform) checkRequirements() error { + err := checkAWSCliVersion() + if err != nil { + return err + } + err = checkIfAWSIsConfigured() + if err != nil { + return err + } + return checkStsCallerIdentity() +} + +func (p eksPlatform) checkCreds() error { + if p.creds.ClusterName == "" || p.creds.AwsRegion == "" { + return errors.New("Incomplete credentials") + } + + authenticated, err := p.authenticateAtCluster() + if err != nil { + return err + } + if !authenticated { + return errors.New("Cannot authenticate at cluster " + p.creds.ClusterName) + } + return nil +} + +func (p eksPlatform) readCreds() { + + connectionSuccessful := false + for !connectionSuccessful { + p.readClusterName() + p.readAwsRegion() + connectionSuccessful, _ = p.authenticateAtCluster() + } +} + +func (p eksPlatform) readClusterName() { + readUserInput(&p.creds.ClusterName, + "^(([a-zA-Z0-9]+-)*[a-zA-Z0-9]+)$", + "Cluster Name", + "Please enter a valid Cluster Name.", + ) +} + +func (p eksPlatform) readAwsRegion() { + readUserInput(&p.creds.AwsRegion, + "^(([a-zA-Z0-9]+-)*[a-zA-Z0-9]+)$", + "AWS Region", + "Please enter a valid AWS Region.", + ) +} + +func (p eksPlatform) authenticateAtCluster() (bool, error) { + _, err := keptnutils.ExecuteCommand("aws", []string{ + "eks", + "--region", + p.creds.AwsRegion, + "update-kubeconfig", + "--name", + p.creds.ClusterName, + }) + + if err != nil { + fmt.Println("Could not connect to cluster. " + + "Please verify that you have entered the correct information. Error: " + err.Error()) + return false, err + } + + return true, nil +} + +type stsCallerIdentity struct { + Account string `json:"Account"` + UserID string `json:"UserId"` + Arn string `json:"Arn"` +} + +func checkStsCallerIdentity() error { + out, err := keptnutils.ExecuteCommand("aws", []string{ + "sts", + "get-caller-identity", + }) + + if err != nil { + return fmt.Errorf("Please configure your sts caller-identity: %s", err) + } + + callerIdentity := &stsCallerIdentity{} + err = json.Unmarshal([]byte(out), &callerIdentity) + if err != nil { + return err + } + if callerIdentity.Account == "" || callerIdentity.UserID == "" || callerIdentity.Arn != "" { + return errors.New("Please check your sts -caller-idenity") + } + return nil +} + +func checkIfAWSIsConfigured() error { + + out, err := keptnutils.ExecuteCommand("aws", []string{ + "configure", "get", "aws_access_key_id", + }) + + if err != nil || out == "" { + return fmt.Errorf("Please configure your aws CLI: %s", err) + } + return nil +} + +func checkAWSCliVersion() error { + + out, err := keptnutils.ExecuteCommand("aws", []string{ + "--version", + }) + + if err != nil { + return err + } + + re := regexp.MustCompile("aws-cli/(.*?) ") + version := strings.TrimSpace(re.FindString(out)) + if version == "" { + return errors.New("Please install the aws CLI at least in version 1.16.156") + } + vNo := strings.Split(version[len("aws-cli/"):], ".") + v1, err := strconv.Atoi(vNo[0]) + if err != nil { + return err + } + v2, err := strconv.Atoi(vNo[1]) + if err != nil { + return err + } + v3, err := strconv.Atoi(vNo[2]) + if err != nil { + return err + } + + if v1 >= 1 && v2 >= 16 && v3 >= 156 { + return nil + } + return errors.New("Please install the AWS CLI at least in version 1.16.156") +} + +func (p eksPlatform) printCreds() { + fmt.Println("Cluster Name: " + p.creds.ClusterName) + fmt.Println("AWS Region: " + p.creds.AwsRegion) +} diff --git a/cli/cmd/installGKE.go b/cli/cmd/installGKE.go index da6f9ce9ac..df3d28289c 100644 --- a/cli/cmd/installGKE.go +++ b/cli/cmd/installGKE.go @@ -54,7 +54,7 @@ func (p gkePlatform) checkRequirements() error { func (p gkePlatform) checkCreds() error { if p.creds.ClusterName == "" || p.creds.ClusterZone == "" { - return errors.New("Incomplete credential file " + *configFilePath) + return errors.New("Incomplete credentials") } authenticated, err := p.authenticateAtCluster() diff --git a/cli/cmd/installOpenShift.go b/cli/cmd/installOpenShift.go index 51f0c517d1..58d1b033a3 100644 --- a/cli/cmd/installOpenShift.go +++ b/cli/cmd/installOpenShift.go @@ -52,7 +52,7 @@ func (p openShiftPlatform) checkRequirements() error { func (p openShiftPlatform) checkCreds() error { if p.creds.OpenshiftURL == "" || p.creds.OpenshiftUser == "" || p.creds.OpenshiftPassword == "" { - return errors.New("Incomplete credential file " + *configFilePath) + return errors.New("Incomplete credentials") } authenticated, err := p.authenticateAtCluster() diff --git a/installer/scripts/eks/creds.sav b/installer/scripts/eks/creds.sav new file mode 100644 index 0000000000..8ab095ca78 --- /dev/null +++ b/installer/scripts/eks/creds.sav @@ -0,0 +1,4 @@ +{ + "clusterName": "CLUSTER_NAME_PLACEHOLDER", + "awsRegion": "AWS_REGION", +} diff --git a/installer/scripts/eks/defineCredentials.sh b/installer/scripts/eks/defineCredentials.sh new file mode 100755 index 0000000000..3142c5ab29 --- /dev/null +++ b/installer/scripts/eks/defineCredentials.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +YLW='\033[1;33m' +NC='\033[0m' + +echo -e "${YLW}Please enter the credentials as requested below: ${NC}" +read -p "Cluster Name: " CLN +read -p "AWS Region: " RG +echo "" + +echo "" +echo -e "${YLW}Please confirm all are correct: ${NC}" +echo "Cluster Name: $CLN" +echo "AWS Region: $RG" +read -p "Is this all correct? (y/n) : " -n 1 -r +echo "" + +if [[ $REPLY =~ ^[Yy]$ ]] +then + CREDS=./creds.json + rm $CREDS 2> /dev/null + cat ./aks/creds.sav | sed 's~CLUSTER_NAME_PLACEHOLDER~'"$CLN"'~' | \ + sed 's~AWS_REGION~'"$RG"'~' >> $CREDS + +fi + +cat $CREDS +echo "" +echo "The credentials file can be found here:" $CREDS +echo "" + diff --git a/installer/scripts/installKeptn.sh b/installer/scripts/installKeptn.sh index db49c85adc..d0fc4c7b02 100755 --- a/installer/scripts/installKeptn.sh +++ b/installer/scripts/installKeptn.sh @@ -10,9 +10,8 @@ case $PLATFORM in ./common/install.sh ;; eks) - echo "$PLATFORM not supported" - echo "Installation aborted" - exit 1 + echo "Install on EKS" + ./common/install.sh ;; openshift) echo "Install on OpenShift"