Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GitHub team allowlist command #1206

Closed

Conversation

jamengual
Copy link
Contributor

@jamengual jamengual commented Oct 1, 2020

Add the ability to specify a allowlist of GitHub teams and Atlantis commands that those teams can execute.

The idea behind this is that an Atlantis operator can pass a parameter to the Atlantis server like so:

atlantis server --gh-team-allowlist "myteam:plan, secteam:apply"

and by doing so add more granular controls on which teams and command those teams can run.

this has been discussed before in this issue : #308 that have 35 👍

Over the years these types of features have been gained a lot of interest for security reasons.

From what I gather there is a big desire for these features to make it to Atlantis and I'm willing to make the necessary changes to make this happen.

I have been using this code for years and I have no issues at all, and other Atlantis users have used this code in their forks.

The current workarounds leave blank comments or require shell scripts or bash parsing solutions.

I have been authorized by CloudPosse to use their code for this PR.

Thanks.

Merged in #1694

@jamengual
Copy link
Contributor Author

@lkysow let me know your thoughts, thanks

@netguino
Copy link
Contributor

netguino commented Nov 6, 2020

I have followed Atlantis for quite some time now, and I recall a conversation, perhaps a PR request, on an Issue, where it was explained that at its core, Atlantis was simply the "executioner" and shouldn't really be evaluating who can do what. I believe it was the same idea when debating about restricting the UI.

While I could potentially agree with this, I wanted to share an end user's perspective.

One of the things that has made Atlantis great for us, has been that it is a pretty self-contained thing. It also isn't a requirement to go through the atlantis route as one can always execute terraform itself if wanted. As such, the vanilla product offers everything we need. Almost. And this almost is what draws the line between a great fit for us, and an exceptional one.

The ability to restrict per github team is one of the missing features.We already restrict what type of conditions need to be in place before executing, so I don't think it's too far of a stretch to mentally assing a "allowed team" structure to the objects. Would it maybe fit better if this was on a per repo setting instead of a global server one?

@nitrocode
Copy link
Member

@netguino I completely agree. This PR has gotten a lot of upvotes. What do you think @lkysow ? Can it be merged?

@jamengual
Copy link
Contributor Author

jamengual commented Nov 6, 2020

The ability to restrict per github team is one of the missing features.We already restrict what type of conditions need to be in place before executing, so I don't think it's too far of a stretch to mentally assing a "allowed team" structure to the objects. Would it maybe fit better if this was on a per repo setting instead of a global server one?

I do not see why we could not create a subsequent PR ( after this is merged) to accommodate this at the per repo level, that is a great idea and it will be easy enough since we already check per user-level access at the repo level so we know the repo that the command is being executed for.

Copy link
Contributor

@nishkrishnan nishkrishnan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. couple comments and questions.

Also, I code cov is dropping and I don't see any tests?

}

// NewTeamAllowlistChecker constructs a new checker
func NewTeamAllowlistChecker(whitelist string) (*TeamAllowlistChecker, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to allowlist

}
for _, t := range teams {
membership, _, err := g.client.Teams.GetTeamMembershipBySlug(g.ctx, org, *t.Slug, user.Username)
//membership, _, err := g.client.Teams.GetTeamMembershipBySlug(g.ctx, org, t.GetID(), user.Username)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably remove this comment


// checkUserPermissions checks if the user has permissions to execute the command
func (e *EventsController) checkUserPermissions(repo models.Repo, user models.User, cmd *events.CommentCommand) (bool, error) {
if cmd.Name == models.ApplyCommand || cmd.Name == models.PlanCommand {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this check? Seems a bit redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we add more command like unlock, it could be parsed by this block which it will not work but it could be removed I think since at that point we know is a valid command

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think by the time we get here the command has been parsed and it is valid. Also this might introduce bugs if there is a new command and this place was not updated. I believe this feature should work with any valid Atlantis command, right?

@jamengual
Copy link
Contributor Author

Looks good. couple comments and questions.

Also, I code cov is dropping and I don't see any tests?

I did not write any test since I did not know how, I was hoping to get a bit of guidance on how to proceed with that since I'm still learning the base code.

@nishkrishnan
Copy link
Contributor

Looks good. couple comments and questions.
Also, I code cov is dropping and I don't see any tests?

I did not write any test since I did not know how, I was hoping to get a bit of guidance on how to proceed with that since I'm still learning the base code.

There are a bunch of examples in the code base and golang in general has good docs on how to write tests/testing best practices

@nishkrishnan nishkrishnan added the needs tests Change requires tests label Jan 28, 2021
@jamengual
Copy link
Contributor Author

Looks good. couple comments and questions.
Also, I code cov is dropping and I don't see any tests?

I did not write any test since I did not know how, I was hoping to get a bit of guidance on how to proceed with that since I'm still learning the base code.

There are a bunch of examples in the code base and golang in general has good docs on how to write tests/testing best practices

yes, I have been looking at those hopefully soon I will start working on them.

@@ -99,6 +100,7 @@ const (
DefaultBitbucketBaseURL = bitbucketcloud.BaseURL
DefaultDataDir = "~/.atlantis"
DefaultGHHostname = "github.com"
DefaultGHTeamAllowlist = "*:*"
Copy link
Contributor

@majormoses majormoses Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather this to something specific (even if its all commands currently supported), and/or force intentional configuration. My rationale is that this makes it safe to implement more dangerous commands that are intentionally not exposed currently such as import and rm. I'd personally prefer a breaking change over backwards compatibility here as whats currently being proposed is users are by default admins. This flies in the face of the https://en.wikipedia.org/wiki/Principle_of_least_privilege. Make admins escalate their privs.

Copy link
Contributor

@msarvar msarvar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, I left couple comments.

@@ -167,6 +169,18 @@ var stringFlags = map[string]stringFlag{
description: "Hostname of your Github Enterprise installation. If using github.com, no need to set.",
defaultValue: DefaultGHHostname,
},
GHTeamAllowlistFlag: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered moving the flag to server config? It might be easier to manage that way. You will also get unmarshaling through server config. It should also remove the need for string parsing that you have to do https://github.com/runatlantis/atlantis/pull/1206/files#diff-6d78b261a5a26bea8298bc9de21e6460e101e2cbc14f0ad282e2415006d0b760R23.

@@ -435,6 +439,20 @@ func (e *EventsController) handleCommentEvent(w http.ResponseWriter, baseRepo mo
return
}

// Check if the user who commented has the permissions to execute the 'plan' or 'apply' commands
if !e.TestingMode {
ok, err := e.checkUserPermissions(baseRepo, user, parseResult.Command)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think moving this into CommandRunnner might work better. If there is ever another way to run atlantis outside of GitHub PRs it will allow developers not to add another check.


// checkUserPermissions checks if the user has permissions to execute the command
func (e *EventsController) checkUserPermissions(repo models.Repo, user models.User, cmd *events.CommentCommand) (bool, error) {
if cmd.Name == models.ApplyCommand || cmd.Name == models.PlanCommand {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think by the time we get here the command has been parsed and it is valid. Also this might introduce bugs if there is a new command and this place was not updated. I believe this feature should work with any valid Atlantis command, right?

@jamengual jamengual requested a review from a team as a code owner June 1, 2021 22:17
@troby
Copy link
Contributor

troby commented Jun 1, 2021

Tests have been added to cover new functions in team_allowlist_checker.go.

@gaston-te
Copy link

@jamengual Is there an ETA for this to be released? This is a feature we're looking forward to

@jamengual
Copy link
Contributor Author

jamengual commented Aug 4, 2021 via email

@paulerickson
Copy link
Contributor

Is there an ETA for this to be released? This is a feature we're looking forward to

@gaston-te, if you want to build it yourself, #1694 is a more up-to-date revision of this.
I am still getting around to the remaining changes, which are mainly refactoring rather than functional.

@majormoses
Copy link
Contributor

majormoses commented Aug 28, 2021

I have followed Atlantis for quite some time now, and I recall a conversation, perhaps a PR request, on an Issue, where it was explained that at its core, Atlantis was simply the "executioner" and shouldn't really be evaluating who can do what. I believe it was the same idea when debating about restricting the UI.

While I could potentially agree with this, I wanted to share an end user's perspective.

One of the things that has made Atlantis great for us, has been that it is a pretty self-contained thing. It also isn't a requirement to go through the atlantis route as one can always execute terraform itself if wanted. As such, the vanilla product offers everything we need. Almost. And this almost is what draws the line between a great fit for us, and an exceptional one.

The ability to restrict per github team is one of the missing features.We already restrict what type of conditions need to be in place before executing, so I don't think it's too far of a stretch to mentally assing a "allowed team" structure to the objects. Would it maybe fit better if this was on a per repo setting instead of a global server one?

Hey I wanted to chime in specifically about atlantis about being the executioner as I was one of those people that said it. Back then there were larger issues for atlantis to solve around its core. I absolutely see the need as a lot of the protections with CI systems are restricting access to the code that can trigger a run, since its essentially remote code execution as a service.

I do disagree with the default of give people apply access. If we are building security features they should be secure by default. We should follow the principle of least privilege and would prefer it to be released as a major version bump and allow people to switch when they can get the required config in place for the migration.

@jamengual
Copy link
Contributor Author

Merged in #1694

@jamengual jamengual closed this Dec 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs tests Change requires tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants