Currently the CI has no good possibility to push changes back to the repository (see gitlab-org/gitlab-ci#105).
Developers have to create separate accounts or SSH keys and make them available to the CI.
Those credentials are available to everyone with read access to the repository (guests) or are shared to other projects using the same runner.
Probably not every build process and user should be allowed to push, so I have two proposals:
Proposal A:
In project settings, allow masters to define, whether or not a build process in this project may push.
If allowed, the generated token gets write access ONLY to the branch, which triggered the build originally.
Example:
Bob can push to his feature branch and triggers a build, which will push changes back to his feature branch. The build process cannot push to master, as it is marked as protected.
Proposal B:
Impersonate/copy the developers rights to the temporary CI token.
This way a build with access to a protected branch will fail if the user does not have access to it.
When run by eligible user the push/build will succeed.
Example:
Bob pushes to the master branch and triggers a build, which in the final step wants to push to several feature branches to keep them in sync. Bob has developer access and the feature branches are marked as protected. This build will fail.
Alice has master access to this project. She restarts the failed build and the protected feature branches will be pushed.
Possible problems:
Pushes can trigger new CI builds and thus lead to infinite loops. Triggering new builds may be or may be not desired in every use-case. Maybe CI-skip should be the default behavior for these.
I would love to see this feature. I've actually just spent the last hour or so trying to figure out how to do it... I guess I should have Googled first .
My usage case is: I have a repo that contains various formats of the same data (YAML, JSON, CSV, XML, etc.). I have a script that take the master YAML file and converts it to the others. Basically every time I push changes I forget to run that conversion script, so I figured I'd try and get CI to run it for me.
@dougthor42: In my envorinment the solution to this was:
Generate a new SSH key
Create a new GitLab account for your runner & add the key to this account
Add the key to the system account the runner runs under
My process to push the changes is:
# for your informationwhoamiprintenv# we need to extract the ssh/git URL as the runner uses a tokenized URLexport CI_PUSH_REPO=`echo$CI_BUILD_REPO | perl -pe's#.*@(.+?(\:\d+)?)/#git@\1:#'`# runner runs on a detached HEAD, create a temporary local branch for editinggit checkout -b ci_processinggit config --global user.name "My Runner"git config --global user.email "runner@gitlab.example.org"git remote set-url --push origin "${CI_PUSH_REPO}"# make your changestouch test.txt# push changes# always return true so that the build does not fail if there are no changesgit push origin ci_processing:${CI_BUILD_REF_NAME}||true
Thanks a lot @l.stoetzel. I had the same "create a runner user account" idea, but I was missing some of the details such as extracting the ssh/git URL and the detached HEAD state. That helps a bunch!
I also realized a few minutes ago that I could also use a git pre-commit or pre-push hook to run the conversion script. It doesn't resolve the proposed GitLab idea (which I'm still in favor of), but it does mean I don't forget to convert my files!
I would probably tend to agree with proposal A, however I would suggest that it be configurable if it is limited to only the branch that was pushed to. Personally, I would like a workflow where developer can tag a commit with some 'staging' tag, and then in my .gitlab-ci.yml I have tasks that run only when tagged with 'staging' that will do all the required tests and actions to bump versions and promote develop to master and push back new versions to both develop and master.
An example from a gitlab-ci.yml using maven and jgitflow using the workaround suggested by @l.stoetzel to demonstrate desired functionality:
Although these work arounds work, they do pose security risks as environment variables or files within the container can be displayed in the build log if the .gitlab-ci.yml is modified to do so, however it would seem that the OAuth secret is always masked.
I would really like this feature so that I can easily compile my pages assets to another branch (a.k.a gl-pages). Compiling to another branch allows me see the contents of the build without downloading an archive every time I want to look, and serves as example output for my app.
This is a feature I would like as well. There are lots of work arounds, but proposal A is nice - allowing a particular runner using the token to push only to that repo it has access to. I personally use it a lot for tagging releases.
gitlab-ce#18994 is on track for shipping in 8.12, but only includes read access. Write access is not planned for 8.12, although it's a likely extension. Because of the security challenges, we want to be a little cautious here and do one step at a time. gitlab-ce#18994 feels safe, and is the right direction. If all goes well, it will evolve.
I will agree that pushing tags from the CI as mentioned in https://gitlab.com/gitlab-org/gitlab-ci/issues/105 is very important. Otherwise, the workaround with SSH keys is not so secure as SSH keys are visible as Variables (or another workaround involving runner image having the key from its ENV in the beginning is needed). Lot of workarounds for pushing tags.
This customer spent several days trying to debug this issue: https://gitlab.my.salesforce.com/00361000016jrse Is there any way for us to provide an error message that might help them understand the issue quicker until we can provide a remedy?
I have the (almost) exact usecase as @andycunn; we want a button to merge to a testing stage, preferably without having to mess around with ssh keys; proposal B seems fine for that purpose (and in line with the current config)
isn't this quite a small thing to add? (as we have gitlab-ce#18994);
maybe for security make it an opt-in in /admin or per-group or something?
p.s.: the current workarounds prevent us from completing our chain (we lose the identity of the dev when the second pipeline is triggered)
I'm pretty sure either would imply traceability, the difference being the actual rights; however I do think that proposal B is more in line with gitlab's way of thinking
Well, it would seem that with gitlab-ce#18994, the approach being taken is closer to Proposal B and given that we already have this functionality where builds use the permissions of the person who pushed I think this should be kept. One of the reasons I was more of an advocate for Proposal A was that I would like to have different permissions for the user and the CI build (i.e. for the release workflow, I don't want to be allowed to push to master from my own machine but if I trigger a release pipeline, I want that pipeline to be able to push to master).
Should this continue, I would like to see an additional option in the protected branch settings which would allow for this protection either through additional roles ("CI Runner") or through an option (checkbox).
I would probably go for the checkbox option (after the Allowed to push: option) similar to Only allow pushes from CI runners such that pushes will be allowed from CI jobs where the owner of the pipeline is listed in the Allowed to push list.
The one limitation of this implementation is that it affect all users, roles and groups and would not allow for the scenario where, for example, a Master would be able to push regardless but developers must push using a CI pipeline. I would think that this scenario is probably a more limited case and in the case where this is true, the weaker permissions can be settled on for now (rather than complicating the UI with an option per role/user/group which is granted permission).
Restrictions on which pipelines are given push permissions may also be required, possibly achieved with a regular expression to match the name of the pipelines which are granted push permissions in the CI token (in my use-case, this would be the release branch).
How do you avoid the loop with proposal A or proposal B? I recently set up a project to push git tags for a prereleases ... but I also pushed the updated version file and started a loop. Since another job calls the GitLab API I wanted to use a token rather than ssh keys.
variables:CI_REPOSITORY_URL:https://$GIT_ACCESS_USER:$GIT_ACCESS_TOKEN@gitlab.com/$CI_PROJECT_PATH.gitscript:-git config --global user.name $GIT_ACCESS_USER-git config --global user.email $GIT_ACCESS_EMAIL-npm run version -- --prerelease $CI_COMMIT_REF_SLUG-git push --follow-tags $CI_REPOSITORY_URL HEAD:$CI_COMMIT_REF_NAME
The Problems:
The push is not tied to the user that triggered the pipeline
If the push updates a file, it triggers another branch based pipeline
Hassle setting up the GIT_ACCESS_TOKEN
The personal access token is long lived and this seems to significantly increase my vulnerability footprint
Proposed Usage:
What about an option in the pipeline settings to select which (regex on job names?) jobs can have write access for the existing CI_JOB_TOKEN?
This would not introduce a new set of variables and would allow users to limit the scope of which jobs (and thus branches/tags) can have the write perms.
This would follow gitlab-ce#18994 but would add the additional exception of the write perms to include the identified jobs and writing to ones own container registry.
The result would have been that the pipelines don't run until the tag occurs, which is exactly when I want the standard pipeline, including deployment, to occur.
It seems to me that there are two aspects to this. One is the granularity of access, e.g. permission to push to master, push only to current branch, push with user's privileges etc. The other is the mechanism by which the push request is secured between the runner and the repo. Currently, this is secured with an asymmetric key pair that the user has to set up and configure, i.e the Deploy Key. I am going to argue that gitlab-ci should set up the keys and a per session/runner basis because it is simpler for the users and more secure.
Currently, to use a Deploy Key, I have to generate a key pair; put the private part in a secret variable, and set the public part as a Deploy Key. Then I have to use the secret variable with the private key to configure ssh on the runner. From a user perspective, this seem like I am setting up the application to authenticate itself to itself, and it is extra work that the user have to learn and get right. Furthermore, it is using a permanent secret for the runner to authenticate itself - as other have mentioned, this can be leaked through log files etc.
I can see no benefit from making users go through all this manual key set up - gitlab could do it itself if the user clicks on the "allow push" checkbox. All that is really needed is a secret session key, which can be a simpler symmetric key. This could be automatically generated and passed to the runner much like the secret variable with the private key is passed currently. The security would be improved because the runner now only has a session key which should be invalidated when the runner finishes the job.
There are various ways this could be implemented, and I am guessing that a way that fits with the existing repo security will be preferable. Therefore, a symmetric shared secret key could be just a long random (session) password. Or if a public/private asymmetric key pair is used then it could be automatically set up in the same way as the current mechanism. (Generating an asymmetric key pair takes longer though, but it would not have to be as long as a permanent asymmetric key pair so a shorter length may offset that a bit.)
The significant improvement is that the users just check a checkbox rather than manage the key set up themselves.
As an addendum to @simon.ward's comment, a very closely related issue to this is installing subpackages on build via something like composer. Right now, the only solution is adding a private ssh key for a user who has access to all required packages for the build to take place. This is easy to manage for individual servers using the deploy keys api and a list of required packages but quite difficult to manage for automated testing where deploy keys have a lifespan of 5 minutes.
Creating an auto-generated key pair would make this much easier not just for Push but also for Pull. I understand that it's out of scope of this specific issue, however, the same solution could be used to solve two different problems. I'd assume package manager support is actually a much more common problem.
I think there are going to be many use cases and workflows.
The gitlab-ci-token was configured to allow for multi-projects when accessing code. So in that case there could be cases where it now also requires the ability to push back to those.
For example I work on a repository that controls keeping things in sync across many other repositories. So it clones down a YAML list of repo's see's if it needs to update anything and then has the ability to "update" those managed repos. (I've added some additional tooling around then have build step that will open a MR for that pushed regex matching branch).
I don't see that as being something that wouldn't be a necessity for others as well and would avoid having to run these steps outside of a CI pipeline.
I like the idea of enabling/disabling allowing pushes from runner as maybe some projects don't want to allow that ability and even going a step further if there was a way to determine which project the runner was run from and give permissions to only a runner that was run via that project.
So for example if I have config_sync repo and it checks repos project1,2,3 you could enable project1,2,3 to only allow push from ci-runner if repo was config_sync... this would disable ANY ci-runner from having push access and restrict down to where the runner came from (probably with one of the other CI_ environment variables).
I store the private part of the SSH deploy key as a secret variable, which I then write to disk temporarily during the CI job pipeline (and remove after of course).
It works pretty well for me so far. (Thanks and credit to the hints from other comments posted above).
@nicolaw I think especially in a Docker container the official way with ssh-agent has several advantages, like not storing the key on disk and having no need to override GIT_SSH_COMMAND.
It shares the disadvantage that SSH are difficult to work with because they contain newlines.
My use case is having a file that contains both data obtained automatically and manual edits. I've set it up so that the manual and automatic edits don't conflict, but having a dedicated user for pushes is quite a bit of micromanagement.
I am not sure of the current status of this ticket or if I have hit this. When I try and push back to the repo I checked out in a CI build I get the following error:
error: src refspec experimental does not match any.error: failed to push some refs to 'https://gitlab-ci-token:xxxxxxxxxxxxxxxxxxxx@gitlab.com/ganges/grpc-ruby-code.git'
The reason I am not sure if I have hit this issue or not is because I have had issues with really vague / misleading error messaging from gitlab around permissions (e.g. returning not found messaging instead of access denied) and I cannot see anyone else posting the actual error that appears for them. So:
Am I hitting the issue described here?
What is the current status on implementing it?
The desribed option B in this issue seems to be the way Gitlab is heading. (e.g. being able to use the token to push an image to the docker container registry or using it to trigger a pipeline from another pipeline in the dev's name)
Incase you want to push changes to a protected branch without fetching all of the repo you can follow the following gist https://gist.github.com/regevbr/cf6d82dba1691123783ccfa22a304f9b which leverages the api to create a branch and then merge it using a pull reques
gitlab-ce#41084 is an proposal to get general more access from within a CI-job with a job-token.
This would include:
the registry (push/pull/delete from and to private/public projects)
Push/Pull to repository (e.g. automatic updates of change logs for automated releases....etc)
API-access to
retrieve Release info,
trigger new pipeline,
issue a deployment token... etc.
Above for all projects where the user who triggered the pipeline has access to!
This proposal would be the ground work to generate an extend token that can be made only available on a safe runner.
And it seems to need an extended job token seen the security issues that would arise from adding more permissions to the current CI-JOB-TOKEN,
whereas the CI-JOB-TOKEN is always made available as an environment variable, also in unsafe executers like shell,ssh and privileged docker containers, allowing malicious users to obtain the token.
And this security concern is probably the main reason why this much needed issue is lying around 2 years with no action!
And of course the public key is added to a user, I'll call the user my-bot for this. So now I should be able to push any changes like the commit and tag npm version produces. Good!
But if I were to push the commit and tag it will then run another pipeline run and I'm in an infinite loop. Since I'm using that "bot" user, I now can check this variable within an except block. Here is an example:
dev build: stage: build except: variables: - $GITLAB_USER_LOGIN == "my-bot" only: - develop@username/reponame before_script: - npm version patch script: - echo "Do build here before pushing the version bump in case the build fails" after_script: - git push git@my-repo-url:$CI_PROJECT_PATH.git HEAD:$CI_COMMIT_REF_NAME --follow-tags
I chose to use $GITLAB_USER_LOGIN instead of $GITLAB_USER_ID because the username is more descriptive and someone else looking at this would understand it better. You could use a pattern to match multiple usernames too if that's your thing. The except is also something I throw into an anchored job and merge it in as I have multiple build jobs for different environments.
As said above, there are multiple ways to change the URL you push to. I chose to inline it within the git push command and even use variables to create things a bit more. Couldn't find a variable that gave me my install page like gitlab.foo.com, only found some with protocol and everything and I didn't want to parse it so it's hardcoded which is fine, don't think that will change.
The only is there to disable the build job(s) in a fork.
Sure... except it only has one ssh key that we actually rotate weekly (it's a build agent machine so the ssh key isn't used in lots of places so easily to swap out). I didn't like people suggesting to add a private variable with $SSH_PRIVATE_KEY, that always seems like a bad idea to me.
How is that any worse or different than what you're doing? If the key is exposed to the CI job then you have to trust that people who can run jobs where it is exposed aren't going to abuse that.
It's not just about trust (our Gitlab is behind a VPN anyway). It's also about managing our rotated keys. Adding a private variable also adds steps to replace it for every CI build that would define that variable. With what I'm doing, I just rotate the keys on the box, update the public key in my gitlab "bot" user and I'm done. Add a new project? Sure, already supported! Also, this process is scripted so it happens via a cron job and while it can be manually triggered, there simply isn't anything anyone has to do. So instead of modifying the script to create or update project variables and create a dependency to update that when a new project is added (that will always be a high risk of being missed especially since at any point the people that know about this setup may leave the project), I just have two things... create a new ssh key pair, update public key on user.
Great, you architected your projects to suit your needs and I'm architecting mine to suit my needs. This is how the world works right? Not all projects are the same . So far, all of my projects need the ssh keys to push to git as they all have that npm version command so I need to push that back up to the git repo. So since all of them need keys, client wants those keys rotated weekly, the need to automate the process via something like a cronjob is needed... for me, maybe not for you. Great. I'm glad you have success!
@mitchellsimoens you also are using docker executor I'm assuming which is great but if you use something like kubernetes to run your executors, unless you bake the SSH keys on every worker node and potential scaled worker node, this method wouldn't scale and would have security implications as previously mentioned.
Gotta love pointing out the except stopping the infinite loop (literally the first sentence in my orig reply) and having people jumping on me for how I'm adding .ssh keys. For me, it works, it works very well and scales for me without any security issues other solution (you know, the $SSH_PRIVATE_KEY solution) would also have. Ok, for something I'm not using the answer would be different but that's for something I'm not using which is pretty obvious from my post.
So thank you for pointing out that my solution would not work for kubernetes as that will no doubt help people using that to give a more clear overall picture and reaffirming that what this feature request is about really needs to be implemented instead of everyone working around it like we are.
@mitchellsimoens everyone has their own solutions... it was merely a matter of raising concerns since there are times people read through other peoples "solutions" and blindly follow... what works for you or me might not work for everyone and also might be "secure" enough for us but definitely not for public consumption. So if anything it was more of a PSA.
I tried to read across this and a few related tickets, but it's a lot of information, so... To quickly summarize this discussion:
There is no official and secure way of pushing back changes to the repository within a gitlab ci workflow, for instance after updating the version of a project.
There are several workarounds which involve adding ssh keys to runners or fiddle with deploy tokens.
There is no known roadmap for implementing an easy way of pushing back changes to the repo.
@All we have self-hosted gitlab on our server. I am not allowed to create a new account for a runner because that entails making a real email on our server and doing many related task to it. I was trying to push from runner using a key pair, by adding public key to the repo's deploy key and using the private key(using a secret variable) in runner job and creating id_rsa file with every possible permission like 600, 700, 644. But it keeps on giving me either permission are too open or public key denied access.
@d-kopriwa Thanks for the reply. I have manually tried the keys on the server and they are working fine if try to push.
about personal access token, can you please enlighten me more? I have used it to push using rest api call but was unsure how to use in the repo, like should I add it in remote or give the whole URL while pushing. Can you tell me how should I do it.
Also, I am wondering if my hosted gitlab have some problem because of the port. git remote set-url origin ssh://git@hostname.com:33022/path/to/repo.git could there be any problem because of it?
I personally changed all our SSH keys with it, because this is the strategy chosen by gitlab-ci by default.
All you need to do is replace the ci token with yours. If you found a SSH pair a better solution, you should use it.
Edit: also, my script is shorter and more flexible than he's, it also work and doesn't need to change the git strategy used by gitlab.
All you need to do is to add the content in a script file and execute it in before script, then you will be able to push normally if you set the GL_TOKEN with your personal access token.
so does the SSH_PRIVATE_KEY so your report doesn't make sense IMO.
SSH keys are not associated with any account, that's the beauty of it. I will just create a key pair on my local machine, upload private key to repo's ci secret variable and public key to deploy keys and deleted it from my machine. So now only people with Master access to the repo and can do harm.
Also if I create personal-access-token with my account, and decide to leave the company, my account will be deactivated and they won't be able to deploy anymore. This doesn't seems like a good approach to me.
Btw, Thanks for the new info, I didn't knew this.
Also, not that when you use a token, the value used for the username doesn't matter at all.
@raghavgarg1257 that's true, I didn't think about the deploy keys feature that's why.
Probably because it requires to configure group or repo and require more work to replace keys on all the repos.
We preferred a single secret that allows the downloads of keys from a http service on the intranet, but we have a user for that and we do not care about creating mailbox for robots.
I still hope they will solve this as this at some point.
While the design decision on this issue isn't made, I'd like to share a snippet that uses admin access to a gitlab server, sets up a bot user with developer access to a project, and adds an impersonation token of this bot to the project variables. Then the repository may be set up to push using that token.
@anton-akhmerov I like this idea. We actually think to use simple release bot for this purpose and sign it as maintainer to groups. As only Contributer can't push to master branches (protected).
But Im wondering of this will support GPG & makes users as deployment commit name owners
@david.mark that all depends on what you do in the container, so you'll need to pass the private key as a secret variable, and correctly configure git to specify the person making the commit.
@anton-akhmerov using "bot" users was discussed all the way at the beginning of the thread as a "workaround" for not being able to impersonate the user that is triggering the CI job when interacting with the projects git repo. In general the approach will do (half) the job but when you are on a pay-for version of Gitlab you will have to pay for every bot user and also there is no way for you to allow different level of access for different branches unless you create more "bot" users which in turn will rake up more licences.
@nielsbne I realize that using a bot user was discussed, and I also realize that it is not the correct in some circumstances, and that even when it works it is merely a workaround.
I shared the bot creation snippet in hope that it might be useful to some, and I apologize if my message left other possible interpretations open.