Pass netrc data to external config service request (#2310)

Co-authored-by: Anbraten <anton@ju60.de>
This commit is contained in:
Pablo Ovelleiro Corral 2023-08-21 13:22:33 +02:00 committed by GitHub
parent 06d1f3c92e
commit 09624aa286
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 33 additions and 10 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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 {
@ -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 {

View file

@ -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,

View file

@ -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:
@ -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),

View file

@ -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)
}

View file

@ -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 {
@ -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)