From e758803b01a889170e03649a34b82352591bffaf Mon Sep 17 00:00:00 2001 From: Mike Metral <1112768+metral@users.noreply.github.com> Date: Tue, 29 Sep 2020 17:49:27 +0000 Subject: [PATCH] fix(stack-cntlr): add SSH keyagent, add keys to known_hosts for SSH git - Enable a SSH keyagent needed for go-git to use the SSH transport during git clones. - Add the public SSH keys for the project repo URL host to ~/.ssh/known_hosts to enforce strict key checking during git clones. --- Dockerfile | 8 ++- README.md | 1 - build/bin/entrypoint | 3 +- deploy/operator_template.yaml | 2 - deploy/yaml/operator.yaml | 2 - examples/blue-green/operator.ts | 1 - go.mod | 1 + go.sum | 2 + pkg/controller/stack/stack_controller.go | 55 ++++++++++++++++++- .../yaml/s3_bucket_stack_basic_auth.yaml | 2 +- stack-examples/yaml/s3_bucket_stack_ssh.yaml | 3 +- 11 files changed, 66 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index c5630a10..82f35903 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,6 @@ -# FROM registry.access.redhat.com/ubi8/ubi-minimal:latest FROM pulumi/pulumi:v2.10.0 ENV OPERATOR=/usr/local/bin/pulumi-kubernetes-operator -# USER_UID=1001 \ -# USER_NAME=pulumi-kubernetes-operator # install operator binary COPY pulumi-kubernetes-operator ${OPERATOR} @@ -12,6 +9,11 @@ COPY build/bin/* /usr/local/bin/ RUN /usr/local/bin/user_setup RUN useradd -m pulumi-kubernetes-operator +RUN mkdir -p /home/pulumi-kubernetes-operator/.ssh \ + && touch /home/pulumi-kubernetes-operator/.ssh/known_hosts \ + && chmod 700 /home/pulumi-kubernetes-operator/.ssh \ + && chown -R pulumi-kubernetes-operator:pulumi-kubernetes-operator /home/pulumi-kubernetes-operator/.ssh + USER pulumi-kubernetes-operator ENTRYPOINT ["/usr/local/bin/entrypoint"] diff --git a/README.md b/README.md index 0d650cf6..a80acc0f 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,6 @@ const operatorDeployment = new kubernetes.apps.v1.Deployment("operatorDeployment containers: [{ name: "pulumi-kubernetes-operator", image: "pulumi/pulumi-kubernetes-operator:v0.0.5", - command: ["pulumi-kubernetes-operator"], args: ["--zap-level=debug"], imagePullPolicy: "Always", env: [ diff --git a/build/bin/entrypoint b/build/bin/entrypoint index 457186bd..98f67cae 100755 --- a/build/bin/entrypoint +++ b/build/bin/entrypoint @@ -1,3 +1,4 @@ #!/bin/sh -e -exec ${OPERATOR} $@ +eval "$(ssh-agent -s)" +exec env SSH_AUTH_SOCK="$SSH_AUTH_SOCK" SSH_AGENT_PID="$SSH_AGENT_PID" "${OPERATOR}" "$@" diff --git a/deploy/operator_template.yaml b/deploy/operator_template.yaml index dc610536..3808515f 100644 --- a/deploy/operator_template.yaml +++ b/deploy/operator_template.yaml @@ -19,8 +19,6 @@ spec: containers: - name: pulumi-kubernetes-operator image: : - command: - - pulumi-kubernetes-operator args: - "--zap-level=debug" imagePullPolicy: Always diff --git a/deploy/yaml/operator.yaml b/deploy/yaml/operator.yaml index b7452712..0f85102c 100644 --- a/deploy/yaml/operator.yaml +++ b/deploy/yaml/operator.yaml @@ -19,8 +19,6 @@ spec: containers: - name: pulumi-kubernetes-operator image: pulumi/pulumi-kubernetes-operator:v0.0.5 - command: - - pulumi-kubernetes-operator args: - "--zap-level=debug" imagePullPolicy: Always diff --git a/examples/blue-green/operator.ts b/examples/blue-green/operator.ts index 2c372ff3..95c53acc 100644 --- a/examples/blue-green/operator.ts +++ b/examples/blue-green/operator.ts @@ -192,7 +192,6 @@ export class PulumiKubernetesOperator extends pulumi.ComponentResource { containers: [{ name: "pulumi-kubernetes-operator", image: "pulumi/pulumi-kubernetes-operator:v0.0.5", - command: ["pulumi-kubernetes-operator"], args: ["--zap-level=debug"], imagePullPolicy: "Always", env: [ diff --git a/go.mod b/go.mod index 3774fab3..a8f20882 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/pulumi/pulumi/sdk/v2 v2.10.1-0.20200915174902-4e6ea760db2e github.com/spf13/pflag v1.0.5 + github.com/whilp/git-urls v1.0.0 golang.org/x/mod v0.3.0 // indirect golang.org/x/tools v0.0.0-20200617161249-6222995d070a // indirect gopkg.in/src-d/go-git.v4 v4.13.1 diff --git a/go.sum b/go.sum index dd1dc2b2..e9dbd9aa 100644 --- a/go.sum +++ b/go.sum @@ -892,6 +892,8 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= +github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= diff --git a/pkg/controller/stack/stack_controller.go b/pkg/controller/stack/stack_controller.go index 5caaf939..bcad6f59 100644 --- a/pkg/controller/stack/stack_controller.go +++ b/pkg/controller/stack/stack_controller.go @@ -21,6 +21,7 @@ import ( pulumiv1alpha1 "github.com/pulumi/pulumi-kubernetes-operator/pkg/apis/pulumi/v1alpha1" "github.com/pulumi/pulumi/sdk/v2/go/x/auto" "github.com/pulumi/pulumi/sdk/v2/go/x/auto/optrefresh" + giturls "github.com/whilp/git-urls" git "gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4/plumbing" corev1 "k8s.io/api/core/v1" @@ -38,6 +39,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" ) @@ -457,8 +459,10 @@ func (sess *reconcileStackSession) runCmd(title string, cmd *exec.Cmd, workspace cmd.Env = os.Environ() } // If there are extra environment variables, set them. - for k, v := range workspace.GetEnvVars() { - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v)) + if workspace != nil { + for k, v := range workspace.GetEnvVars() { + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v)) + } } // Capture stdout and stderr. @@ -738,6 +742,10 @@ func (sess *reconcileStackSession) SetupGitAuth(namespace string) (*auto.GitAuth if password, exists := secret.Data["password"]; exists { gitAuth.Password = string(password) } + + // Add the project repo's public SSH keys to the SSH known hosts + // to perform the necessary key checking during SSH git cloning. + sess.addSSHKeysToKnownHosts(sess.stack.ProjectRepo) // Then check if a personal access token has been specified. } else if accessToken, exists := secret.Data["accessToken"]; exists { gitAuth = &auto.GitAuth{ @@ -822,6 +830,49 @@ func (sess *reconcileStackSession) waitForDeletion(o runtime.Object) error { }, ctx.Done()) } +// addSSHKeysToKnownHosts scans the public SSH keys for the project repository URL +// and adds them to the SSH known hosts to perform strict key checking during SSH +// git cloning. +func (sess *reconcileStackSession) addSSHKeysToKnownHosts(projectRepoURL string) error { + // Parse the Stack project repo SSH host and port (if exists) from the git SSH URL + // e.g. git@github.com:foo/bar.git returns "github.com" for host + // e.g. git@example.com:1234:foo/bar.git returns "example.com" for host and "1234" for port + u, err := giturls.Parse(projectRepoURL) + if err != nil { + return errors.Wrap(err, "error parsing project repo URL to use with ssh-keyscan") + } + hostPort := strings.Split(u.Host, ":") + if len(hostPort) == 0 || len(hostPort) > 2 { + return errors.Wrap(err, "error parsing project repo URL to use with ssh-keyscan") + } + + // SSH key scan the repo's URL (host port) to get the public keys. + args := []string{} + if len(hostPort) == 2 { + args = append(args, "-p", hostPort[1]) + } + args = append(args, "-H", hostPort[0]) + sshKeyScan, _ := exec.LookPath("ssh-keyscan") + cmd := exec.Command(sshKeyScan, args...) + cmd.Dir = os.Getenv("HOME") + stdout, _, err := sess.runCmd("SSH Key Scan", cmd, nil) + if err != nil { + return errors.Wrap(err, "error running ssh-keyscan") + } + + // Add the repo public keys to the SSH known hosts to enforce key checking. + filename := fmt.Sprintf("%s/%s", os.Getenv("HOME"), ".ssh/known_hosts") + f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + return errors.Wrap(err, "error running ssh-keyscan") + } + defer f.Close() + if _, err = f.WriteString(stdout); err != nil { + return errors.Wrap(err, "error running ssh-keyscan") + } + return nil +} + func contains(list []string, s string) bool { for _, v := range list { if v == s { diff --git a/stack-examples/yaml/s3_bucket_stack_basic_auth.yaml b/stack-examples/yaml/s3_bucket_stack_basic_auth.yaml index 7038d8ee..8c037d58 100644 --- a/stack-examples/yaml/s3_bucket_stack_basic_auth.yaml +++ b/stack-examples/yaml/s3_bucket_stack_basic_auth.yaml @@ -34,7 +34,7 @@ spec: - pulumi-aws-secrets gitAuthSecret: git-secret stack: joeduffy/s3-op-project/dev - projectRepo: git@github.com:joeduffy/test-s3-op-project.git + projectRepo: https://github.com/joeduffy/test-s3-op-project commit: cc5442870f1195216d6bc340c14f8ae7d28cf3e2 config: aws:region: us-east-2 diff --git a/stack-examples/yaml/s3_bucket_stack_ssh.yaml b/stack-examples/yaml/s3_bucket_stack_ssh.yaml index 28806ef4..68e694bd 100644 --- a/stack-examples/yaml/s3_bucket_stack_ssh.yaml +++ b/stack-examples/yaml/s3_bucket_stack_ssh.yaml @@ -12,8 +12,9 @@ metadata: name: git-secret type: Opaque data: + # need to base64 SSH private key to set a valid YAML value sshPrivateKey: "" - password: "" + password: "" --- apiVersion: v1 kind: Secret