diff --git a/docs/content/en/docs/operator-manual/piped/configuration-reference.md b/docs/content/en/docs/operator-manual/piped/configuration-reference.md index 86663f8c41..b90e0f940d 100644 --- a/docs/content/en/docs/operator-manual/piped/configuration-reference.md +++ b/docs/content/en/docs/operator-manual/piped/configuration-reference.md @@ -22,6 +22,7 @@ spec: | projectID | string | The identifier of the PipeCD project where this piped belongs to. | Yes | | pipedID | string | The generated ID for this piped. | Yes | | pipedKeyFile | string | The path to the file containing the generated key string for this piped. | Yes | +| pipedKeyData | string | Base64 encoded string of Piped key. Either pipedKeyFile or pipedKeyData must be set. | Yes | | apiAddress | string | The address used to connect to the control-plane's API. | Yes | | webAddress | string | The address to the control-plane's Web. | No | | syncInterval | duration | How often to check whether an application should be synced. Default is `1m`. | No | @@ -43,6 +44,7 @@ spec: | host | string | The host name. Default is `github.com`. | No | | hostName | string | The hostname or IP address of the remote git server. Default is the same value with Host. | No | | sshKeyFile | string | The path to the private ssh key file. This will be used to clone the source code of the specified git repositories. | No | +| sshKeyData | string | Base64 encoded string of SSH key. | No | ## GitRepository diff --git a/pkg/app/piped/cmd/piped/piped.go b/pkg/app/piped/cmd/piped/piped.go index 4b7b69306d..752db7f499 100644 --- a/pkg/app/piped/cmd/piped/piped.go +++ b/pkg/app/piped/cmd/piped/piped.go @@ -174,8 +174,14 @@ func (p *piped) run(ctx context.Context, t cli.Telemetry) (runErr error) { } } + pipedKey, err := cfg.LoadPipedKey() + if err != nil { + t.Logger.Error("failed to load piped key", zap.Error(err)) + return err + } + // Make gRPC client and connect to the API. - apiClient, err := p.createAPIClient(ctx, cfg.APIAddress, cfg.ProjectID, cfg.PipedID, cfg.PipedKeyFile, t.Logger) + apiClient, err := p.createAPIClient(ctx, cfg.APIAddress, cfg.ProjectID, cfg.PipedID, pipedKey, t.Logger) if err != nil { t.Logger.Error("failed to create gRPC client to control plane", zap.Error(err)) return err @@ -428,19 +434,13 @@ func (p *piped) run(ctx context.Context, t cli.Telemetry) (runErr error) { } // createAPIClient makes a gRPC client to connect to the API. -func (p *piped) createAPIClient(ctx context.Context, address, projectID, pipedID, pipedKeyFile string, logger *zap.Logger) (pipedservice.Client, error) { +func (p *piped) createAPIClient(ctx context.Context, address, projectID, pipedID string, pipedKey []byte, logger *zap.Logger) (pipedservice.Client, error) { if p.useFakeAPIClient { return pipedclientfake.NewClient(logger), nil } ctx, cancel := context.WithTimeout(ctx, 15*time.Second) defer cancel() - pipedKey, err := ioutil.ReadFile(pipedKeyFile) - if err != nil { - logger.Error("failed to read piped key file", zap.Error(err)) - return nil, err - } - var ( token = rpcauth.MakePipedToken(projectID, pipedID, string(pipedKey)) creds = rpcclient.NewPerRPCCredentials(token, rpcauth.PipedTokenCredentials, !p.insecure) diff --git a/pkg/config/piped.go b/pkg/config/piped.go index 7e1f72f9ab..3c3108661c 100644 --- a/pkg/config/piped.go +++ b/pkg/config/piped.go @@ -15,9 +15,11 @@ package config import ( + "encoding/base64" "encoding/json" "errors" "fmt" + "os" "github.com/pipe-cd/pipe/pkg/model" ) @@ -36,6 +38,8 @@ type PipedSpec struct { PipedID string // The path to the file containing the generated Key string for this piped. PipedKeyFile string + // Base64 encoded string of Piped key. + PipedKeyData string // The address used to connect to the control-plane's API. APIAddress string `json:"apiAddress"` // The address to the control-plane's Web. @@ -189,6 +193,19 @@ func (s *PipedSpec) GetSecretManagement() *SecretManagement { return s.SecretManagement } +func (s *PipedSpec) LoadPipedKey() ([]byte, error) { + if s.PipedKeyData != "" && s.PipedKeyFile != "" { + return nil, errors.New("only either pipedKeyFile or pipedKeyData can be set") + } + if s.PipedKeyData != "" { + return base64.StdEncoding.DecodeString(s.PipedKeyData) + } + if s.PipedKeyFile != "" { + return os.ReadFile(s.PipedKeyFile) + } + return nil, errors.New("either pipedKeyFile or pipedKeyData must be set") +} + type PipedGit struct { // The username that will be configured for `git` user. // Default is "piped". @@ -210,10 +227,25 @@ type PipedGit struct { // The path to the private ssh key file. // This will be used to clone the source code of the specified git repositories. SSHKeyFile string `json:"sshKeyFile"` + // Base64 encoded string of ssh-key. + SSHKeyData string `json:"sshKeyData"` } func (g PipedGit) ShouldConfigureSSHConfig() bool { - return g.SSHKeyFile != "" + return g.SSHKeyData != "" || g.SSHKeyFile != "" +} + +func (g PipedGit) LoadSSHKey() ([]byte, error) { + if g.SSHKeyData != "" && g.SSHKeyFile != "" { + return nil, errors.New("only either sshKeyFile or sshKeyData can be set") + } + if g.SSHKeyData != "" { + return base64.StdEncoding.DecodeString(g.SSHKeyData) + } + if g.SSHKeyFile != "" { + return os.ReadFile(g.SSHKeyFile) + } + return nil, errors.New("either sshKeyFile or sshKeyData must be set") } type PipedRepository struct { diff --git a/pkg/git/ssh_config.go b/pkg/git/ssh_config.go index cc2a18369a..67c21359ad 100644 --- a/pkg/git/ssh_config.go +++ b/pkg/git/ssh_config.go @@ -63,24 +63,22 @@ func AddSSHConfig(cfg config.PipedGit) error { return fmt.Errorf("failed to create a directory %s: %v", sshDir, err) } - var sshKeyFile string - if cfg.SSHKeyFile != "" { - f, err := os.CreateTemp(sshDir, "piped-ssh-key-*") - if err != nil { - return err - } - key, err := os.ReadFile(cfg.SSHKeyFile) - if err != nil { - return err - } - // TODO: Remove this key file when Piped terminating. - if _, err := f.Write(key); err != nil { - return err - } - sshKeyFile = f.Name() + sshKey, err := cfg.LoadSSHKey() + if err != nil { + return err + } + + sshKeyFile, err := os.CreateTemp(sshDir, "piped-ssh-key-*") + if err != nil { + return err + } + + // TODO: Remove this key file when Piped terminating. + if _, err := sshKeyFile.Write(sshKey); err != nil { + return err } - configData, err := generateSSHConfig(cfg, sshKeyFile) + configData, err := generateSSHConfig(cfg, sshKeyFile.Name()) if err != nil { return err }