diff --git a/cmd/server.go b/cmd/server.go index 3fc9ff1aaf..67a37fa8ac 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -32,8 +32,9 @@ import ( // 3. Add your flag's description etc. to the stringFlags, intFlags, or boolFlags slices. const ( // Flag names. - AtlantisURLFlag = "atlantis-url" AllowForkPRsFlag = "allow-fork-prs" + AllowRepoConfigFlag = "allow-repo-config" + AtlantisURLFlag = "atlantis-url" ConfigFlag = "config" DataDirFlag = "data-dir" GHHostnameFlag = "gh-hostname" @@ -142,6 +143,13 @@ var boolFlags = []boolFlag{ description: "Allow Atlantis to run on pull requests from forks. A security issue for public repos.", defaultValue: false, }, + { + name: AllowRepoConfigFlag, + description: "Allow repositories to use atlantis.yaml files to customize the commands Atlantis runs." + + " Should only be enabled in a trusted environment since it enables a pull request to run arbitrary commands" + + " on the Atlantis server.", + defaultValue: false, + }, { name: RequireApprovalFlag, description: "Require pull requests to be \"Approved\" before allowing the apply command to be run.", @@ -298,8 +306,9 @@ func (s *ServerCmd) run() error { // Config looks good. Start the server. server, err := s.ServerCreator.NewServer(userConfig, server.Config{ - AllowForkPRsFlag: AllowForkPRsFlag, - AtlantisVersion: s.AtlantisVersion, + AllowForkPRsFlag: AllowForkPRsFlag, + AllowRepoConfigFlag: AllowRepoConfigFlag, + AtlantisVersion: s.AtlantisVersion, }) if err != nil { return errors.Wrap(err, "initializing server") diff --git a/runatlantis.io/docs/atlantis-yaml-reference.md b/runatlantis.io/docs/atlantis-yaml-reference.md index 9301f72aa5..a2fbe21755 100644 --- a/runatlantis.io/docs/atlantis-yaml-reference.md +++ b/runatlantis.io/docs/atlantis-yaml-reference.md @@ -9,6 +9,10 @@ See [www.runatlantis.io/guide/atlantis-yaml-use-cases.html](../guide/atlantis-yaml-use-cases.html) ::: +## Enabling atlantis.yaml +The atlantis server must be running with `--allow-repo-config` to allow Atlantis +to use `atlantis.yaml` files. + ## Example Using All Keys ```yaml version: 2 diff --git a/scripts/e2e.sh b/scripts/e2e.sh index a1d3a44eea..02c6021658 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -8,7 +8,7 @@ ${CIRCLE_WORKING_DIRECTORY}/scripts/e2e-deps.sh cd "${CIRCLE_WORKING_DIRECTORY}/e2e" # start atlantis server in the background and wait for it to start -./atlantis server --gh-user="$GITHUB_USERNAME" --gh-token="$GITHUB_PASSWORD" --data-dir="/tmp" --log-level="debug" --repo-whitelist="github.com/runatlantis/atlantis-tests" &> /tmp/atlantis-server.log & +./atlantis server --gh-user="$GITHUB_USERNAME" --gh-token="$GITHUB_PASSWORD" --data-dir="/tmp" --log-level="debug" --repo-whitelist="github.com/runatlantis/atlantis-tests" --allow-repo-config &> /tmp/atlantis-server.log & sleep 2 # start ngrok in the background and wait for it to start diff --git a/server/events/command_runner_test.go b/server/events/command_runner_test.go index 5f0d2ce4c0..0b1518c09c 100644 --- a/server/events/command_runner_test.go +++ b/server/events/command_runner_test.go @@ -41,7 +41,6 @@ var githubGetter *mocks.MockGithubPullGetter var gitlabGetter *mocks.MockGitlabMergeRequestGetter var ch events.DefaultCommandRunner var logBytes *bytes.Buffer -var projectCommandRunner *mocks.MockProjectCommandRunner func setup(t *testing.T) { RegisterMockTestingT(t) diff --git a/server/events/project_command_builder.go b/server/events/project_command_builder.go index a530ee31d9..549bbb3172 100644 --- a/server/events/project_command_builder.go +++ b/server/events/project_command_builder.go @@ -24,12 +24,14 @@ type ProjectCommandBuilder interface { } type DefaultProjectCommandBuilder struct { - ParserValidator *yaml.ParserValidator - ProjectFinder ProjectFinder - VCSClient vcs.ClientProxy - WorkingDir WorkingDir - WorkingDirLocker WorkingDirLocker - RequireApproval bool + ParserValidator *yaml.ParserValidator + ProjectFinder ProjectFinder + VCSClient vcs.ClientProxy + WorkingDir WorkingDir + WorkingDirLocker WorkingDirLocker + RequireApproval bool + AllowRepoConfig bool + AllowRepoConfigFlag string } type TerraformExec interface { @@ -63,6 +65,9 @@ func (p *DefaultProjectCommandBuilder) BuildAutoplanCommands(ctx *CommandContext ctx.Log.Info("found no %s file", yaml.AtlantisYAMLFilename) } else { ctx.Log.Info("successfully parsed %s file", yaml.AtlantisYAMLFilename) + if !p.AllowRepoConfig { + return nil, fmt.Errorf("%s files not allowed because Atlantis is not running with --%s", yaml.AtlantisYAMLFilename, p.AllowRepoConfigFlag) + } } // We'll need the list of modified files. @@ -201,6 +206,9 @@ func (p *DefaultProjectCommandBuilder) getCfg(projectName string, dir string, wo if !hasAtlantisYAML { return nil, nil, nil } + if !p.AllowRepoConfig { + return nil, nil, fmt.Errorf("%s files not allowed because Atlantis is not running with --%s", yaml.AtlantisYAMLFilename, p.AllowRepoConfigFlag) + } // If they've specified a project by name we look it up. Otherwise we // use the dir and workspace. diff --git a/server/events/project_command_builder_test.go b/server/events/project_command_builder_test.go index 111e790f40..1a50de40be 100644 --- a/server/events/project_command_builder_test.go +++ b/server/events/project_command_builder_test.go @@ -195,6 +195,7 @@ projects: ParserValidator: &yaml.ParserValidator{}, VCSClient: vcsClient, ProjectFinder: &events.DefaultProjectFinder{}, + AllowRepoConfig: true, } ctxs, err := builder.BuildAutoplanCommands(&events.CommandContext{ @@ -416,6 +417,7 @@ projects: ParserValidator: &yaml.ParserValidator{}, VCSClient: vcsClient, ProjectFinder: &events.DefaultProjectFinder{}, + AllowRepoConfig: true, } cmdCtx := &events.CommandContext{ diff --git a/server/events/terraform/terraform_client.go b/server/events/terraform/terraform_client.go index ce290b13e2..bd2e6de704 100644 --- a/server/events/terraform/terraform_client.go +++ b/server/events/terraform/terraform_client.go @@ -42,7 +42,6 @@ type DefaultClient struct { const terraformPluginCacheDirName = "plugin-cache" // zeroPointNine constrains the version to be 0.9.* -var zeroPointNine = MustConstraint(">=0.9,<0.10") var versionRegex = regexp.MustCompile("Terraform v(.*)\n") func NewClient(dataDir string) (*DefaultClient, error) { diff --git a/server/events_controller_e2e_test.go b/server/events_controller_e2e_test.go index 00048b63b4..b50215b3e8 100644 --- a/server/events_controller_e2e_test.go +++ b/server/events_controller_e2e_test.go @@ -270,11 +270,14 @@ func setupE2E(t *testing.T) (server.EventsController, *vcsmocks.MockClientProxy, AllowForkPRs: allowForkPRs, AllowForkPRsFlag: "allow-fork-prs", ProjectCommandBuilder: &events.DefaultProjectCommandBuilder{ - ParserValidator: &yaml.ParserValidator{}, - ProjectFinder: &events.DefaultProjectFinder{}, - VCSClient: e2eVCSClient, - WorkingDir: workingDir, - WorkingDirLocker: locker, + ParserValidator: &yaml.ParserValidator{}, + ProjectFinder: &events.DefaultProjectFinder{}, + VCSClient: e2eVCSClient, + WorkingDir: workingDir, + WorkingDirLocker: locker, + AllowRepoConfigFlag: "allow-repo-config", + AllowRepoConfig: true, + RequireApproval: false, }, } diff --git a/server/server.go b/server/server.go index efeddef900..0d7c595932 100644 --- a/server/server.go +++ b/server/server.go @@ -80,6 +80,7 @@ type Server struct { // the config is parsed from a YAML file. type UserConfig struct { AllowForkPRs bool `mapstructure:"allow-fork-prs"` + AllowRepoConfig bool `mapstructure:"allow-repo-config"` AtlantisURL string `mapstructure:"atlantis-url"` DataDir string `mapstructure:"data-dir"` GithubHostname string `mapstructure:"gh-hostname"` @@ -104,8 +105,9 @@ type UserConfig struct { // Config holds config for server that isn't passed in by the user. type Config struct { - AllowForkPRsFlag string - AtlantisVersion string + AllowForkPRsFlag string + AllowRepoConfigFlag string + AtlantisVersion string } // WebhookConfig is nested within UserConfig. It's used to configure webhooks. @@ -232,12 +234,14 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { AllowForkPRs: userConfig.AllowForkPRs, AllowForkPRsFlag: config.AllowForkPRsFlag, ProjectCommandBuilder: &events.DefaultProjectCommandBuilder{ - ParserValidator: &yaml.ParserValidator{}, - ProjectFinder: &events.DefaultProjectFinder{}, - VCSClient: vcsClient, - WorkingDir: workingDir, - WorkingDirLocker: workingDirLocker, - RequireApproval: userConfig.RequireApproval, + ParserValidator: &yaml.ParserValidator{}, + ProjectFinder: &events.DefaultProjectFinder{}, + VCSClient: vcsClient, + WorkingDir: workingDir, + WorkingDirLocker: workingDirLocker, + RequireApproval: userConfig.RequireApproval, + AllowRepoConfig: userConfig.AllowRepoConfig, + AllowRepoConfigFlag: config.AllowRepoConfigFlag, }, ProjectCommandRunner: &events.DefaultProjectCommandRunner{ Locker: projectLocker,