Skip to content

Commit

Permalink
Pass netrc data to external config service request (#2310)
Browse files Browse the repository at this point in the history
Co-authored-by: Anbraten <anton@ju60.de>
  • Loading branch information
pinpox and anbraten authored Aug 21, 2023
1 parent 06d1f3c commit 09624aa
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 10 deletions.
4 changes: 4 additions & 0 deletions docs/docs/30-administration/100-external-configuration-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Every request sent by Woodpecker is signed using a [http-signature](https://data

A simplistic example configuration service can be found here: [https://github.com/woodpecker-ci/example-config-service](https://github.com/woodpecker-ci/example-config-service)

:::warning
You need to trust the external config service as it is getting secret information about the repository and pipeline and has the ability to change pipeline configs that could run malicious tasks.
:::

## Config

```shell
Expand Down
7 changes: 6 additions & 1 deletion server/api/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,12 @@ func PostPipeline(c *gin.Context) {
}
}

newpipeline, err := pipeline.Restart(c, _store, pl, user, repo, envs)
netrc, err := server.Config.Services.Forge.Netrc(user, repo)
if err != nil {
handlePipelineErr(c, err)
}

newpipeline, err := pipeline.Restart(c, _store, pl, user, repo, envs, netrc)
if err != nil {
handlePipelineErr(c, err)
} else {
Expand Down
13 changes: 9 additions & 4 deletions server/forge/configFetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,15 @@ func (cf *configFetcher) Fetch(ctx context.Context) (files []*types.FileMeta, er
defer cancel() // ok here as we only try http fetching once, returning on fail and success

log.Trace().Msgf("ConfigFetch[%s]: getting config from external http service", cf.repo.FullName)
newConfigs, useOld, err := cf.configExtension.FetchConfig(fetchCtx, cf.repo, cf.pipeline, files)
netrc, err := cf.forge.Netrc(cf.user, cf.repo)
if err != nil {
log.Error().Msg("Got error " + err.Error())
return nil, fmt.Errorf("On Fetching config via http : %w", err)
return nil, fmt.Errorf("could not get Netrc data from forge: %w", err)
}

newConfigs, useOld, err := cf.configExtension.FetchConfig(fetchCtx, cf.repo, cf.pipeline, files, netrc)
if err != nil {
log.Error().Err(err).Msg("could not fetch config via http")
return nil, fmt.Errorf("could not fetch config via http: %w", err)
}

if !useOld {
Expand Down Expand Up @@ -109,7 +114,7 @@ func (cf *configFetcher) fetch(c context.Context, timeout time.Duration, config
return nil, fmt.Errorf("user defined config '%s' not found: %w", config, err)
}

log.Trace().Msgf("ConfigFetch[%s]: user did not defined own config, following default procedure", cf.repo.FullName)
log.Trace().Msgf("ConfigFetch[%s]: user did not define own config, following default procedure", cf.repo.FullName)
// for the order see shared/constants/constants.go
fileMeta, err := cf.getFirstAvailableConfig(ctx, constant.DefaultConfigOrder[:], false)
if err == nil {
Expand Down
2 changes: 2 additions & 0 deletions server/forge/configFetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ func TestFetchFromConfigService(t *testing.T) {
f.On("File", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("File not found"))
f.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("Directory not found"))

f.On("Netrc", mock.Anything, mock.Anything).Return(&model.Netrc{Machine: "mock", Login: "mock", Password: "mock"}, nil)

configFetcher := forge.NewConfigFetcher(
f,
time.Second*3,
Expand Down
4 changes: 2 additions & 2 deletions server/pipeline/restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

// Restart a pipeline by creating a new one out of the old and start it
func Restart(ctx context.Context, store store.Store, lastPipeline *model.Pipeline, user *model.User, repo *model.Repo, envs map[string]string) (*model.Pipeline, error) {
func Restart(ctx context.Context, store store.Store, lastPipeline *model.Pipeline, user *model.User, repo *model.Repo, envs map[string]string, netrc *model.Netrc) (*model.Pipeline, error) {
switch lastPipeline.Status {
case model.StatusDeclined,
model.StatusBlocked:
Expand Down Expand Up @@ -58,7 +58,7 @@ func Restart(ctx context.Context, store store.Store, lastPipeline *model.Pipelin
currentFileMeta[i] = &forge_types.FileMeta{Name: cfg.Name, Data: cfg.Data}
}

newConfig, useOld, err := server.Config.Services.ConfigService.FetchConfig(ctx, repo, lastPipeline, currentFileMeta)
newConfig, useOld, err := server.Config.Services.ConfigService.FetchConfig(ctx, repo, lastPipeline, currentFileMeta, netrc)
if err != nil {
return nil, &ErrBadRequest{
Msg: fmt.Sprintf("On fetching external pipeline config: %s", err),
Expand Down
2 changes: 1 addition & 1 deletion server/plugins/config/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ import (

type Extension interface {
IsConfigured() bool
FetchConfig(ctx context.Context, repo *model.Repo, pipeline *model.Pipeline, currentFileMeta []*forge_types.FileMeta) (configData []*forge_types.FileMeta, useOld bool, err error)
FetchConfig(ctx context.Context, repo *model.Repo, pipeline *model.Pipeline, currentFileMeta []*forge_types.FileMeta, netrc *model.Netrc) (configData []*forge_types.FileMeta, useOld bool, err error)
}
11 changes: 9 additions & 2 deletions server/plugins/config/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type requestStructure struct {
Repo *model.Repo `json:"repo"`
Pipeline *model.Pipeline `json:"pipeline"`
Configuration []*config `json:"configs"`
Netrc *model.Netrc `json:"netrc"`
}

type responseStructure struct {
Expand All @@ -53,14 +54,20 @@ func (cp *http) IsConfigured() bool {
return cp.endpoint != ""
}

func (cp *http) FetchConfig(ctx context.Context, repo *model.Repo, pipeline *model.Pipeline, currentFileMeta []*forge_types.FileMeta) (configData []*forge_types.FileMeta, useOld bool, err error) {
func (cp *http) FetchConfig(ctx context.Context, repo *model.Repo, pipeline *model.Pipeline, currentFileMeta []*forge_types.FileMeta, netrc *model.Netrc) (configData []*forge_types.FileMeta, useOld bool, err error) {
currentConfigs := make([]*config, len(currentFileMeta))
for i, pipe := range currentFileMeta {
currentConfigs[i] = &config{Name: pipe.Name, Data: string(pipe.Data)}
}

response := new(responseStructure)
body := requestStructure{Repo: repo, Pipeline: pipeline, Configuration: currentConfigs}
body := requestStructure{
Repo: repo,
Pipeline: pipeline,
Configuration: currentConfigs,
Netrc: netrc,
}

status, err := utils.Send(ctx, "POST", cp.endpoint, cp.privateKey, body, response)
if err != nil && status != 204 {
return nil, false, fmt.Errorf("Failed to fetch config via http (%d) %w", status, err)
Expand Down

0 comments on commit 09624aa

Please sign in to comment.