Merge lp:~dooferlad/offspring/linaro_offspring_add_bzr_ssh into lp:~linaro-automation/offspring/linaro
- linaro_offspring_add_bzr_ssh
- Merge into linaro
Status: | Rejected |
---|---|
Rejected by: | Guilherme Salgado |
Proposed branch: | lp:~dooferlad/offspring/linaro_offspring_add_bzr_ssh |
Merge into: | lp:~linaro-automation/offspring/linaro |
Diff against target: |
616 lines (+401/-13) 13 files modified
lib/offspring/build/bin/offspring-build (+4/-0) lib/offspring/build/functions/exit.sh (+3/-0) lib/offspring/build/functions/ssh.sh (+23/-0) lib/offspring/build/tests/offspring_test_key (+27/-0) lib/offspring/build/tests/offspring_test_key.pub (+1/-0) lib/offspring/build/tests/test.sh (+24/-0) lib/offspring/build/wrappers/test.sh (+52/-0) lib/offspring/master/models.py (+11/-2) lib/offspring/slave/slave.py (+58/-10) lib/offspring/slave/tests/test_private_ssh_keys.py (+110/-0) lib/offspring/web/queuemanager/admin.py (+3/-1) lib/offspring/web/queuemanager/forms.py (+78/-0) lib/offspring/web/queuemanager/models.py (+7/-0) |
To merge this branch: | bzr merge lp:~dooferlad/offspring/linaro_offspring_add_bzr_ssh |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guilherme Salgado (community) | Needs Fixing | ||
Review via email: mp+78279@code.launchpad.net |
This proposal supersedes a proposal from 2011-09-29.
Commit message
Description of the change
This merge address the comments raised about the initial one. It also updates the web UI so the admin interface allows the user to change the ssh user and key, but can't get access to the stored key.
-
I have an SQL migration script ready, but I will submit it on top of salgado's change that introduces the migration directory.
This change adds support for adding an SSH user and private key to an Offspring project on creation. The key is written to a temporary file, the location of which is passed to the builder, which then loads it into a new instance of ssh-agent, which is killed after the build is finished. At this point the key file is deleted.
The SSH user is inserted into the bzr branch URL. lp: URLs are supported, as are bzr+ssh.
As yet I have not modified the admin interface. You can add a project through the non-admin UI and from there add a user + SSH key. On the admin interface you can't see the key (which has to be not pass phrase protected) or user name. I am planning on adding a sensible way of updating the user and key without leaking the key back out of the interface tomorrow, at which point I think this feature will be complete.
Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal | # |
James Tunnicliffe (dooferlad) wrote : Posted in a previous version of this proposal | # |
Hi,
Thanks for the feedback.
On 30 September 2011 20:07, Guilherme Salgado
<email address hidden> wrote:
>> The SSH user is inserted into the bzr branch URL. lp: URLs are supported, as are bzr+ssh.
>
> Did you consider calling "bzr lp-login <username>" right after running
> ssh-add instead of changing the branch URL when starting the build?
> That would be simpler to implement and more flexible as it'd allow any
> URL schemes, but it'd also allow the build script to fetch other
> branches from LP using the same user.
That is a very good point. I didn't consider that.
I guess using bzr lp-login may be a bit irritating when testing
because it would keep logging us out.
I am not familiar with the mechanics of how lp-login works, but it
seems to use ~/.bazaar/
information:
[Launchpad]
host = .launchpad.net
scheme = ssh
user = dooferlad
I can't see any way of overriding this using an environment variable.
If several slaves were being run as the same user, sharing a home
directory, they would all use the user set by the last process to
start so that would cause us some problems.
http://
http://
http://
A quick skim of these says that all URL formats accept the form:
bzr branch <scheme>
So we are fine embedding the user in the URL in general and I change
lp:~ to the long form as a separate step, so the current code is safe.
I agree that a more simple solution would be nice though. Will let you
know if I think of anything better.
>> As yet I have not modified the admin interface. You can add a project through the non-admin UI and from there add a user + SSH key. On the admin interface you can't see the key (which has to be not pass phrase protected) or user name. I am planning on adding a sensible way of updating the user and key without leaking the key back out of the interface tomorrow, at which point I think this feature will be complete.
>> differences between files attachment (review-diff.txt)
>> === modified file 'lib/offspring/
>> --- lib/offspring/
>> +++ lib/offspring/
>> @@ -168,10 +168,39 @@
>> Stop_logging
>> Catch_faults disable
>>
>> + if [ $RUN_SSH_AGENT = 1 ]; then
>> + echo kill
>
> Was this just for debugging?
Oops, yes. Will delete it!
>> + kill $SSH_AGENT_PID
>> + fi
>
> Given the security implications of failing to kill the ssh agent, I
> think it's really important that we have a test for this. Do you have
> any idea how hard it'd be to write one?
I think it is possible, but I am not sure how simple it will be. I
think it will involve:
1. Start a new, clean shell (assert that no ssh-agent is running in it)
2. Run a job that checks that ssh-agent is running and somehow returns
information about this to the test runner.
3. Check that no ssh-agent is running
If the new shell is created...
Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal | # |
On Mon, 2011-10-03 at 08:31 +0000, James Tunnicliffe wrote:
> Hi,
>
> Thanks for the feedback.
>
> On 30 September 2011 20:07, Guilherme Salgado
> <email address hidden> wrote:
> >> The SSH user is inserted into the bzr branch URL. lp: URLs are supported, as are bzr+ssh.
> >
> > Did you consider calling "bzr lp-login <username>" right after running
> > ssh-add instead of changing the branch URL when starting the build?
> > That would be simpler to implement and more flexible as it'd allow any
> > URL schemes, but it'd also allow the build script to fetch other
> > branches from LP using the same user.
>
> That is a very good point. I didn't consider that.
>
> I guess using bzr lp-login may be a bit irritating when testing
> because it would keep logging us out.
But it would (automatically) login again at the start of every build,
no?
>
> I am not familiar with the mechanics of how lp-login works, but it
> seems to use ~/.bazaar/
> information:
>
> [Launchpad]
> host = .launchpad.net
> scheme = ssh
> user = dooferlad
>
> I can't see any way of overriding this using an environment variable.
> If several slaves were being run as the same user, sharing a home
> directory, they would all use the user set by the last process to
> start so that would cause us some problems.
But I don't think we'll ever want to share home directories across
slaves because that could allow a rogue job to taint others or have
access to private information. I think it's safe to assume the slaves
don't share anything other than an NFS mount where they put the built
artifacts.
I'm CCing James W. to confirm.
>
> http://
> http://
> http://
>
> A quick skim of these says that all URL formats accept the form:
> bzr branch <scheme>
> So we are fine embedding the user in the URL in general and I change
> lp:~ to the long form as a separate step, so the current code is safe.
> I agree that a more simple solution would be nice though. Will let you
> know if I think of anything better.
>
> >> As yet I have not modified the admin interface. You can add a project through the non-admin UI and from there add a user + SSH key. On the admin interface you can't see the key (which has to be not pass phrase protected) or user name. I am planning on adding a sensible way of updating the user and key without leaking the key back out of the interface tomorrow, at which point I think this feature will be complete.
> >> differences between files attachment (review-diff.txt)
> >> === modified file 'lib/offspring/
> >> --- lib/offspring/
> >> +++ lib/offspring/
> >> @@ -168,10 +168,39 @@
> >> Stop_logging
> >> Catch_faults disable
> >>
> >> + if [ $RUN_SSH_AGENT = 1 ]; then
> >> + echo kill
> >
> > Was this just for debugging?
>
> Oops, yes. Will delete it!
> ...
James Tunnicliffe (dooferlad) wrote : Posted in a previous version of this proposal | # |
On 3 October 2011 15:40, Guilherme Salgado <email address hidden> wrote:
> On Mon, 2011-10-03 at 08:31 +0000, James Tunnicliffe wrote:
>> Hi,
>>
>> Thanks for the feedback.
>>
>> On 30 September 2011 20:07, Guilherme Salgado
>> <email address hidden> wrote:
>> >> The SSH user is inserted into the bzr branch URL. lp: URLs are supported, as are bzr+ssh.
>> >
>> > Did you consider calling "bzr lp-login <username>" right after running
>> > ssh-add instead of changing the branch URL when starting the build?
>> > That would be simpler to implement and more flexible as it'd allow any
>> > URL schemes, but it'd also allow the build script to fetch other
>> > branches from LP using the same user.
>>
>> That is a very good point. I didn't consider that.
>>
>> I guess using bzr lp-login may be a bit irritating when testing
>> because it would keep logging us out.
>
> But it would (automatically) login again at the start of every build,
> no?
I was mostly thinking of testing on my machine here (not wanting my
user to be logged out of lp), but it would be simple enough to set up
another user to run the slave.
>>
>> I am not familiar with the mechanics of how lp-login works, but it
>> seems to use ~/.bazaar/
>> information:
>>
>> [Launchpad]
>> host = .launchpad.net
>> scheme = ssh
>> user = dooferlad
>>
>> I can't see any way of overriding this using an environment variable.
>> If several slaves were being run as the same user, sharing a home
>> directory, they would all use the user set by the last process to
>> start so that would cause us some problems.
>
> But I don't think we'll ever want to share home directories across
> slaves because that could allow a rogue job to taint others or have
> access to private information. I think it's safe to assume the slaves
> don't share anything other than an NFS mount where they put the built
> artifacts.
Sounds good to me.
> I'm CCing James W. to confirm.
>
>>
>> http://
>> http://
>> http://
>>
>> A quick skim of these says that all URL formats accept the form:
>> bzr branch <scheme>
>> So we are fine embedding the user in the URL in general and I change
>> lp:~ to the long form as a separate step, so the current code is safe.
>> I agree that a more simple solution would be nice though. Will let you
>> know if I think of anything better.
>>
>> >> As yet I have not modified the admin interface. You can add a project through the non-admin UI and from there add a user + SSH key. On the admin interface you can't see the key (which has to be not pass phrase protected) or user name. I am planning on adding a sensible way of updating the user and key without leaking the key back out of the interface tomorrow, at which point I think this feature will be complete.
>> >> differences between files attachment (review-diff.txt)
>> >> === modified file 'lib/offspring/
>> >> --- lib/offspring/
James Westby (james-w) wrote : Posted in a previous version of this proposal | # |
On Mon, 03 Oct 2011 14:40:28 -0000, Guilherme Salgado <email address hidden> wrote:
> But I don't think we'll ever want to share home directories across
> slaves because that could allow a rogue job to taint others or have
> access to private information. I think it's safe to assume the slaves
> don't share anything other than an NFS mount where they put the built
> artifacts.
>
> I'm CCing James W. to confirm.
That I'm not 100% sure on. Probably worth asking Cody or IS.
Multiple builds will be serialised to the same slave, so there is
certainly sharing of homedirs there, but reliably cleaning up after a
build would avoid problems with that, if it can indeed be done reliably.
Is even having access to the build artefacts going to be an issue for
member services?
Also, while I remember, be sure to take note of the added dependencies
when requesting a rollout of this code, as if e.g. ssh-agent is missing
on a machine where it needs to be it may break existing builds.
> That was my concern, but on a second thought, that shouldn't be possible
> as Offspring should not run any commands other than what's in the build
> script (IIUC). Although it might be possible to, say, craft a hwpack
> config that causes linaro-
If this is used for building images with live-build, then that is
trivially possible.
Thanks,
James
James Tunnicliffe (dooferlad) wrote : Posted in a previous version of this proposal | # |
On 3 October 2011 18:25, James Westby <email address hidden> wrote:
> On Mon, 03 Oct 2011 14:40:28 -0000, Guilherme Salgado <email address hidden> wrote:
>> But I don't think we'll ever want to share home directories across
>> slaves because that could allow a rogue job to taint others or have
>> access to private information. I think it's safe to assume the slaves
>> don't share anything other than an NFS mount where they put the built
>> artifacts.
>>
>> I'm CCing James W. to confirm.
>
> That I'm not 100% sure on. Probably worth asking Cody or IS.
>
> Multiple builds will be serialised to the same slave, so there is
> certainly sharing of homedirs there, but reliably cleaning up after a
> build would avoid problems with that, if it can indeed be done reliably.
I take it each offspring-slave instance runs as its own user, thus
having its own home directory then? If this is the case, then it
should solve the ssh-agent vulnerability.
> Is even having access to the build artefacts going to be an issue for
> member services?
I imagine that if the final output of the build is private, it is safe
to assume that anything to do with it should be private.
> Also, while I remember, be sure to take note of the added dependencies
> when requesting a rollout of this code, as if e.g. ssh-agent is missing
> on a machine where it needs to be it may break existing builds.
ssh-agent is installed with openssh-client, so it should be installed
already. I have put a WI down to make sure it gets into the docs
though.
>> That was my concern, but on a second thought, that shouldn't be possible
>> as Offspring should not run any commands other than what's in the build
>> script (IIUC). Although it might be possible to, say, craft a hwpack
>> config that causes linaro-
>
> If this is used for building images with live-build, then that is
> trivially possible.
...so we are going to have to be careful to clean up after ourselves.
It doesn't sound trivial since the build process can write to ~, /tmp
and possibly some other places. Deleting all files owned by the user
from a list of locations would be one way of going about it, but I
think I would set up a VM to test it in!
--
James Tunnicliffe
James Westby (james-w) wrote : Posted in a previous version of this proposal | # |
On Tue, 4 Oct 2011 13:59:45 +0100, James Tunnicliffe <email address hidden> wrote:
> I take it each offspring-slave instance runs as its own user, thus
> having its own home directory then? If this is the case, then it
> should solve the ssh-agent vulnerability.
I imagine they run as the same username on different machines, so if
they are not sharing /home on NFS or similar it should be ok, but I
don't know if they are.
> > Is even having access to the build artefacts going to be an issue for
> > member services?
>
> I imagine that if the final output of the build is private, it is safe
> to assume that anything to do with it should be private.
Yeah, I guess it's a question of whether we can trust the people who
have access to the config to not try and take over a build and get at
artefacts from a different project.
> > Also, while I remember, be sure to take note of the added dependencies
> > when requesting a rollout of this code, as if e.g. ssh-agent is missing
> > on a machine where it needs to be it may break existing builds.
>
> ssh-agent is installed with openssh-client, so it should be installed
> already. I have put a WI down to make sure it gets into the docs
> though.
Thanks. There's also django-
> >> That was my concern, but on a second thought, that shouldn't be possible
> >> as Offspring should not run any commands other than what's in the build
> >> script (IIUC). Although it might be possible to, say, craft a hwpack
> >> config that causes linaro-
> >
> > If this is used for building images with live-build, then that is
> > trivially possible.
>
> ...so we are going to have to be careful to clean up after ourselves.
> It doesn't sound trivial since the build process can write to ~, /tmp
> and possibly some other places. Deleting all files owned by the user
> from a list of locations would be one way of going about it, but I
> think I would set up a VM to test it in!
A scheme that didn't rely on such a cleanup would be good, but may not
be feasible.
Thanks,
James
Guilherme Salgado (salgado) wrote : | # |
On Wed, 2011-10-05 at 16:25 +0000, James Tunnicliffe wrote:
> James Tunnicliffe has proposed merging lp:~dooferlad/offspring/linaro_offspring_add_bzr_ssh into lp:~linaro-infrastructure/offspring/linaro.
>
> Requested reviews:
> Linaro Infrastructure (linaro-
>
> For more details, see:
> https:/
>
> This merge address the comments raised about the initial one. It also
> updates the web UI so the admin interface allows the user to change
> the ssh user and key, but can't get access to the stored key.
I was expecting the ssh user/key to be editable on the project's edit
page (the project_edit() view), but I guess that wouldn't work because
people with the change_project permission would then be able to change
that for all projects, right?
Having to use the admin UI to change a project's ssh user/key means the
people in charge of every project will have to send the ssh key to
somebody with admin rights to set. I think we should check with Vicky
that this is ok.
> I have an SQL migration script ready, but I will submit it on top of
> salgado's change that introduces the migration directory.
>
> This change adds support for adding an SSH user and private key to an
> Offspring project on creation. The key is written to a temporary file,
> the location of which is passed to the builder, which then loads it
> into a new instance of ssh-agent, which is killed after the build is
> finished. At this point the key file is deleted.
>
> The SSH user is inserted into the bzr branch URL. lp: URLs are
> supported, as are bzr+ssh.
> === modified file 'lib/offspring/
> --- lib/offspring/
> +++ lib/offspring/
> @@ -35,6 +35,7 @@
> source "${IBS_
> source "${IBS_
> source "${IBS_
> + source "${IBS_
>
> [ -n "${IBS_PROXY}" ] && export http_proxy=
>
> @@ -195,6 +196,9 @@
> echo "Performing initialization..."
> _Initialize
>
> +# If any SSH keys have been passed to us, load them into ssh-agent
> +AddKeys ${3}
> +
> # Setup and start logging
> _Setup
>
>
> === modified file 'lib/offspring/
> --- lib/offspring/
> +++ lib/offspring/
> @@ -61,6 +61,9 @@
> Action_done
> fi
>
> + # Always stop SSH agent...
> + StopSSHAgent
> +
> Action "Remvoving build statefile"
> rm ${IBS_BUILDER_
> Action_done
>
> === added file 'lib/offspring/
> --- lib/offspring/
> +++ lib/offspring/
> @@ -0,0 +1,23 @@
> +#!/bin/bash
> +
> +# Copyright 2010 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +AddKeys() {
> + RUN_SSH_AGENT=1
> +
> + if [ -z "${1}" ]; then
> + ...
James Tunnicliffe (dooferlad) wrote : | # |
Hi Guilherme,
Thanks for another review. Seem to be getting close now.
On 6 October 2011 14:28, Guilherme Salgado <email address hidden> wrote:
> On Wed, 2011-10-05 at 16:25 +0000, James Tunnicliffe wrote:
>> James Tunnicliffe has proposed merging lp:~dooferlad/offspring/linaro_offspring_add_bzr_ssh into lp:~linaro-infrastructure/offspring/linaro.
>> This merge address the comments raised about the initial one. It also
>> updates the web UI so the admin interface allows the user to change
>> the ssh user and key, but can't get access to the stored key.
>
> I was expecting the ssh user/key to be editable on the project's edit
> page (the project_edit() view), but I guess that wouldn't work because
> people with the change_project permission would then be able to change
> that for all projects, right?
>
> Having to use the admin UI to change a project's ssh user/key means the
> people in charge of every project will have to send the ssh key to
> somebody with admin rights to set. I think we should check with Vicky
> that this is ok.
Ah, sorry, that comment was wrong - anyone can paste a new SSH key
into the project page, it isn't just the admin page.
Am I right in thinking that the code you are putting in will restrict
access to all the project pages to specified users? If so, I think
this is OK.
<snip>
>> === modified file 'lib/offspring/
>> - def startBuild(self, projectName, configBranchURL):
>> + def add_user_
>> + # check for lp: format URLs. Convert these to bzr+ssh ones
>> + configBranchURL = re.sub("lp:", "bzr+ssh:
>> + configBranchURL)
>
> I thought you were going to use 'bzr lp-login' as it's not a problem if
> we fail to cleanup ~/.bazaar/
> to kill the ssh agent. And if we fail to kill the ssh agent then using
> lp-login is not much worse than it is now.
The last comment James W made about home directories was that they may
be shared between builders, so we don't want to transport the
launchpad user using ~/.bazaar/
because it could change before it is used.
<snip>
>> + def startBuild(self, projectName, configBranchURL, lpUser=None, lpSSHKey=None):
>> +
>> + if lpUser:
>> + # We don't want to pass the user separately, we want it to be
>> + # integrated into the configBranchURL.
>> + configBranchURL = self.add_
>> +
>> self.currentBuild = ProjectBuildPro
>> - self, projectName, configBranchURL)
>> + self, projectName, configBranchURL, lpSSHKey)
>> logging.
>> self.currentBui
>> self.currentBui
>> @@ -70,9 +94,12 @@
>> if self.currentBui
>> if self.currentBui
>> self.currentBui
>> - # Give it 5 seconds before sending kill.
...
Guilherme Salgado (salgado) wrote : | # |
On Thu, 2011-10-06 at 14:37 +0000, James Tunnicliffe wrote:
> Hi Guilherme,
>
> Thanks for another review. Seem to be getting close now.
Indeed, if it wasn't for the permission issue we just seem to have
uncovered... :(
> On 6 October 2011 14:28, Guilherme Salgado <email address hidden> wrote:
> > On Wed, 2011-10-05 at 16:25 +0000, James Tunnicliffe wrote:
> >> James Tunnicliffe has proposed merging lp:~dooferlad/offspring/linaro_offspring_add_bzr_ssh into lp:~linaro-infrastructure/offspring/linaro.
>
> >> This merge address the comments raised about the initial one. It also
> >> updates the web UI so the admin interface allows the user to change
> >> the ssh user and key, but can't get access to the stored key.
> >
> > I was expecting the ssh user/key to be editable on the project's edit
> > page (the project_edit() view), but I guess that wouldn't work because
> > people with the change_project permission would then be able to change
> > that for all projects, right?
> >
> > Having to use the admin UI to change a project's ssh user/key means the
> > people in charge of every project will have to send the ssh key to
> > somebody with admin rights to set. I think we should check with Vicky
> > that this is ok.
>
> Ah, sorry, that comment was wrong - anyone can paste a new SSH key
> into the project page, it isn't just the admin page.
>
> Am I right in thinking that the code you are putting in will restrict
> access to all the project pages to specified users? If so, I think
> this is OK.
Only access to private projects will be restricted to specified users.
> >> === modified file 'lib/offspring/
> >> - def startBuild(self, projectName, configBranchURL):
> >> + def add_user_
> >> + # check for lp: format URLs. Convert these to bzr+ssh ones
> >> + configBranchURL = re.sub("lp:", "bzr+ssh:
> >> + configBranchURL)
> >
> > I thought you were going to use 'bzr lp-login' as it's not a problem if
> > we fail to cleanup ~/.bazaar/
> > to kill the ssh agent. And if we fail to kill the ssh agent then using
> > lp-login is not much worse than it is now.
>
> The last comment James W made about home directories was that they may
> be shared between builders, so we don't want to transport the
> launchpad user using ~/.bazaar/
> because it could change before it is used.
My understanding is that they are only shared between builds on a
certain builder, which is not a problem as they're serialized and we're
killing ssh-agent between builds.
> >> + def startBuild(self, projectName, configBranchURL, lpUser=None, lpSSHKey=None):
> >> +
> >> + if lpUser:
> >> + # We don't want to pass the user separately, we want it to be
> >> + # integrated into the configBranchURL.
> >> + configBranchURL = self.add_
> >> +
> >> self.currentBuild = ProjectBuildPro
> >> - self, projectName, configBranchURL)
> >> + self, pr...
James Tunnicliffe (dooferlad) wrote : | # |
On 6 October 2011 17:08, Guilherme Salgado <email address hidden> wrote:
>> The last comment James W made about home directories was that they may
>> be shared between builders, so we don't want to transport the
>> launchpad user using ~/.bazaar/
>> because it could change before it is used.
>
> My understanding is that they are only shared between builds on a
> certain builder, which is not a problem as they're serialized and we're
> killing ssh-agent between builds.
I guess I am worried that a builder could be a multi-core machine and
all instances of a user could share 1 home directory on that machine.
Basically, the current solution is always safe, no matter what the
disk situation is and using lp-login is with the current set up, but
may not be in the future. I would just rather go with something that
is more robust.
> The problem is that there are no per-project permissions in Offspring;
> if a user can change one project they can change all of them (modulo the
> private ones, after our work is complete).
>
> If we keep it only in the admin UI we'd have to give the ssh keys to
> someone with admin rights, and this someone would then be given the ssh
> keys of all landing teams.
>
> Or maybe we could have one member of each LT as an admin in Offspring so
> that they can do it themselves. With your changes to not show the
> existing ssh key that means they'd only be able to enter new ssh keys
> for projects but not see existing ones. I don't think it is a good idea
> to have one person from each LT with admin rights though.
How about:
* When creating a project, you can enter an SSH key and user
* Once a project is created...
- If it is private, you can change the SSH key (current interface)
- If it is public, you can't change the SSH key
* The admin interface always allows you to change the SSH key
Another option would be disabling SSH users + keys for all public
projects and leave them as they are for private ones once both our
patches are working together.
The only other option In can think of is recording the user that
created the project and, if it is public, only letting that user
change the SSH user and key. For private projects we could give the
project group permission.
What do you think?
--
James Tunnicliffe
- 66. By James Tunnicliffe
-
Made ProjectBuildPro
cess.args into a local variable for __init__ (not required for testing)
Removed null test from test_private_ssh_keys. py - 67. By James Tunnicliffe
-
Made failures in test_checkSSHAgent easier to debug by providing sensible error messages.
Guilherme Salgado (salgado) wrote : | # |
On Thu, 2011-10-06 at 17:31 +0000, James Tunnicliffe wrote:
> On 6 October 2011 17:08, Guilherme Salgado <email address hidden> wrote:
> >> The last comment James W made about home directories was that they may
> >> be shared between builders, so we don't want to transport the
> >> launchpad user using ~/.bazaar/
> >> because it could change before it is used.
> >
> > My understanding is that they are only shared between builds on a
> > certain builder, which is not a problem as they're serialized and we're
> > killing ssh-agent between builds.
>
> I guess I am worried that a builder could be a multi-core machine and
> all instances of a user could share 1 home directory on that machine.
> Basically, the current solution is always safe, no matter what the
> disk situation is and using lp-login is with the current set up, but
> may not be in the future. I would just rather go with something that
> is more robust.
Fair enough
>
> > The problem is that there are no per-project permissions in Offspring;
> > if a user can change one project they can change all of them (modulo the
> > private ones, after our work is complete).
> >
> > If we keep it only in the admin UI we'd have to give the ssh keys to
> > someone with admin rights, and this someone would then be given the ssh
> > keys of all landing teams.
> >
> > Or maybe we could have one member of each LT as an admin in Offspring so
> > that they can do it themselves. With your changes to not show the
> > existing ssh key that means they'd only be able to enter new ssh keys
> > for projects but not see existing ones. I don't think it is a good idea
> > to have one person from each LT with admin rights though.
>
> How about:
> * When creating a project, you can enter an SSH key and user
> * Once a project is created...
> - If it is private, you can change the SSH key (current interface)
> - If it is public, you can't change the SSH key
> * The admin interface always allows you to change the SSH key
Sounds good to me, and maybe we can even allow editing ssh keys of
public projects... Changing the ssh key of an existing project won't
give an attacker access to anything they don't already have access to
anyway.
>
> Another option would be disabling SSH users + keys for all public
> projects and leave them as they are for private ones once both our
> patches are working together.
>
> The only other option In can think of is recording the user that
> created the project and, if it is public, only letting that user
> change the SSH user and key. For private projects we could give the
> project group permission.
>
> What do you think?
>
--
Guilherme Salgado <https:/
James Tunnicliffe (dooferlad) wrote : | # |
On 7 October 2011 00:43, Guilherme Salgado <email address hidden> wrote:
> On Thu, 2011-10-06 at 17:31 +0000, James Tunnicliffe wrote:
>> How about:
>> * When creating a project, you can enter an SSH key and user
>> * Once a project is created...
>> - If it is private, you can change the SSH key (current interface)
>> - If it is public, you can't change the SSH key
>> * The admin interface always allows you to change the SSH key
>
> Sounds good to me, and maybe we can even allow editing ssh keys of
> public projects... Changing the ssh key of an existing project won't
> give an attacker access to anything they don't already have access to
> anyway.
OK, so I think if we are going to do this, we should merge in my
current changes then add in logic using your private projects stuff to
allow the customisation of the project interface. As it is, the pages
are safe (SSH key never shown) and with the private project controls
we can disable editing SSH credentials for public projects.
Having reviewed your code I see that you have the concept of a project
owner, which seems to be the person who created the project. Perhaps
we can always give the project owner the ability to change SSH keys
for public projects?
I am not entirely sure if we need to worry about locking down the
ability to change SSH keys and users for public projects though -
anyone with access to the Offspring instance can already come along
and mess up a project's settings after all. The only difference with
SSH keys is it may be more difficult to revert any changes because the
private key may be a more closely guarded secret than the rather
public config branch URL and other settings.
--
James Tunnicliffe
Guilherme Salgado (salgado) wrote : | # |
On Thu, 2011-10-13 at 13:01 +0000, James Tunnicliffe wrote:
> On 7 October 2011 00:43, Guilherme Salgado <email address hidden> wrote:
> > On Thu, 2011-10-06 at 17:31 +0000, James Tunnicliffe wrote:
>
> >> How about:
> >> * When creating a project, you can enter an SSH key and user
> >> * Once a project is created...
> >> - If it is private, you can change the SSH key (current interface)
> >> - If it is public, you can't change the SSH key
> >> * The admin interface always allows you to change the SSH key
> >
> > Sounds good to me, and maybe we can even allow editing ssh keys of
> > public projects... Changing the ssh key of an existing project won't
> > give an attacker access to anything they don't already have access to
> > anyway.
>
> OK, so I think if we are going to do this, we should merge in my
> current changes then add in logic using your private projects stuff to
> allow the customisation of the project interface. As it is, the pages
> are safe (SSH key never shown) and with the private project controls
> we can disable editing SSH credentials for public projects.
We don't necessarily need to merge your changes into trunk -- you could
just branch off of my private-projects branch, merge your changes and
then work on the new stuff. That way we never need to block on some
changes landing into trunk.
It is important that you start a new branch for those extra changes
because then we can discuss and review them separately. In fact, it'd
have been nice if you'd used a separate branch for the UI changes you
already have on this branch. ;)
> Having reviewed your code I see that you have the concept of a project
> owner, which seems to be the person who created the project. Perhaps
Right, that's necessary because otherwise it'd be more complicated to
ensure there's always someone who can edit the ACLs of the project.
> we can always give the project owner the ability to change SSH keys
> for public projects?
I'm not sure we need to restrict it only to the project owner (if that's
what you're suggesting?), because there's not any real damage one can do
by changing the SSH key of a project. The only problem is if they were
allowed to see the existing ssh key, but that you've already taken care
of.
> I am not entirely sure if we need to worry about locking down the
> ability to change SSH keys and users for public projects though -
> anyone with access to the Offspring instance can already come along
> and mess up a project's settings after all. The only difference with
In fact it's only those users with the change_project permission that
can do that...
> SSH keys is it may be more difficult to revert any changes because the
> private key may be a more closely guarded secret than the rather
> public config branch URL and other settings.
Agreed.
I'll do another review because I haven't looked at the UI changes yet.
Guilherme Salgado (salgado) wrote : | # |
Hm, there seems to be a failing test on this branch.
./.virtualenv/
F...
=======
FAIL: test_checkSSHAgent (test_private_
-------
Traceback (most recent call last):
File "/home/
"Identity added: <path to...>/
AssertionError: Test build ssh-agent check failed: Expected to find Identity added: <path to...>/
@@@@@@@
-------
root: INFO: Starting up offspring slave server on port 8760
root: DEBUG: Request timeout set to 60.0 seconds
root: INFO: Slave ready to accept builds
-------
-------
Ran 4 tests in 2.049s
FAILED (failures=1)
Guilherme Salgado (salgado) wrote : | # |
Hi James,
Here's another round of review. Some trivial things I've missed in the
previous one, but I think the UI needs to be reworked; see below for details.
Don't forget to add the SQL migration script. After merging from the new
private-builds branch you'll have a migrations/ toplevel directory and you can
just add your script there.
> === modified file 'lib/offspring/
> --- lib/offspring/
> +++ lib/offspring/
> @@ -57,9 +60,30 @@
> self.server.stop()
> self.server.join()
>
> - def startBuild(self, projectName, configBranchURL):
> + def add_user_
I've missed this in the first review, but I think Offspring in general uses
camelCase for method names; would you mind renaming this to be consistency
with others?
> + # check for lp: format URLs. Convert these to bzr+ssh ones
> + configBranchURL = re.sub("lp:", "bzr+ssh:
> + configBranchURL)
> +
> + # The assumption is that this function will only be called with a
> + # bzr+ssh URL (hence the name!)
> + assert(
> +
> + # Add user to URL
> + configBranchURL = re.sub(
> + "bzr+ssh://" + lpUser + "@", configBranchURL)
> +
> + return configBranchURL
> +
> + def startBuild(self, projectName, configBranchURL, lpUser=None, lpSSHKey=None):
> +
> + if lpUser:
> + # We don't want to pass the user separately, we want it to be
> + # integrated into the configBranchURL.
> + configBranchURL = self.add_
> +
> self.currentBuild = ProjectBuildPro
> - self, projectName, configBranchURL)
> + self, projectName, configBranchURL, lpSSHKey)
> logging.
> self.currentBui
> self.currentBui
> @@ -159,14 +186,34 @@
>
> class ProjectBuildPro
>
> - def __init__(self, supervisor, projectName, configBranchURL):
> - MonitoredProces
> - self, supervisor, [projectName, configBranchURL],
> + def __init__(self, supervisor, projectName, configBranchURL, lpSSHKey=None):
> + args = [projectName, configBranchURL]
> + self.ssh_key_file = None
> +
> + if lpSSHKey:
> + # Write SSH key to disk. tempfile will delete it after the build
> + # when this class goes out of scope.
> + # Need to have this here so we can pass the temp file name
> + # containing the key to the builder process on its command line.
> + self.ssh_key_file = tempfile.
> + args.append(
> + self.ssh_
> + self.ssh_
> +
> + MonitoredProces
Nitpick: the indentation here was ...
Unmerged revisions
- 67. By James Tunnicliffe
-
Made failures in test_checkSSHAgent easier to debug by providing sensible error messages.
- 66. By James Tunnicliffe
-
Made ProjectBuildPro
cess.args into a local variable for __init__ (not required for testing)
Removed null test from test_private_ssh_keys. py - 65. By James Tunnicliffe
-
Moved slave tests to their own directory.
- 64. By James Tunnicliffe
-
Added test for ssh-agent interaction.
- 63. By James Tunnicliffe
-
Tidied up tests.
- 62. By James Tunnicliffe
-
A bit of tidying up.
Mostly removed support for adding multiple SSH keys to ssh-agent via offspring-build since this was unnecessary and would just complicate testing. - 61. By James Tunnicliffe
-
Fixed up tests for validating new SSH keys.
- 60. By James Tunnicliffe
-
Adding support for SSH key input on admin interface without showing existing keys.
- 59. By James Tunnicliffe
-
Made lp_user a more reasonable type and size.
- 58. By James Tunnicliffe
-
Auto create SSH key file and delete it when build process terminates.
Preview Diff
1 | === modified file 'lib/offspring/build/bin/offspring-build' |
2 | --- lib/offspring/build/bin/offspring-build 2011-09-21 21:06:01 +0000 |
3 | +++ lib/offspring/build/bin/offspring-build 2011-10-06 17:54:25 +0000 |
4 | @@ -35,6 +35,7 @@ |
5 | source "${IBS_FUNCDIR}/reports.sh" |
6 | source "${IBS_FUNCDIR}/logging.sh" |
7 | source "${IBS_FUNCDIR}/files.sh" |
8 | + source "${IBS_FUNCDIR}/ssh.sh" |
9 | |
10 | [ -n "${IBS_PROXY}" ] && export http_proxy="${IBS_PROXY}" |
11 | |
12 | @@ -195,6 +196,9 @@ |
13 | echo "Performing initialization..." |
14 | _Initialize |
15 | |
16 | +# If any SSH keys have been passed to us, load them into ssh-agent |
17 | +AddKeys ${3} |
18 | + |
19 | # Setup and start logging |
20 | _Setup |
21 | |
22 | |
23 | === modified file 'lib/offspring/build/functions/exit.sh' |
24 | --- lib/offspring/build/functions/exit.sh 2010-11-29 08:27:24 +0000 |
25 | +++ lib/offspring/build/functions/exit.sh 2011-10-06 17:54:25 +0000 |
26 | @@ -61,6 +61,9 @@ |
27 | Action_done |
28 | fi |
29 | |
30 | + # Always stop SSH agent... |
31 | + StopSSHAgent |
32 | + |
33 | Action "Remvoving build statefile" |
34 | rm ${IBS_BUILDER_STATEFILE} |
35 | Action_done |
36 | |
37 | === added file 'lib/offspring/build/functions/ssh.sh' |
38 | --- lib/offspring/build/functions/ssh.sh 1970-01-01 00:00:00 +0000 |
39 | +++ lib/offspring/build/functions/ssh.sh 2011-10-06 17:54:25 +0000 |
40 | @@ -0,0 +1,23 @@ |
41 | +#!/bin/bash |
42 | + |
43 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
44 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
45 | + |
46 | +AddKeys() { |
47 | + RUN_SSH_AGENT=1 |
48 | + |
49 | + if [ -z "${1}" ]; then |
50 | + let RUN_SSH_AGENT=-1 |
51 | + fi |
52 | + |
53 | + if [ $RUN_SSH_AGENT = 1 ]; then |
54 | + eval `ssh-agent` |
55 | + ssh-add $1 |
56 | + fi |
57 | +} |
58 | + |
59 | +StopSSHAgent() { |
60 | + if [ $RUN_SSH_AGENT = 1 ]; then |
61 | + kill $SSH_AGENT_PID |
62 | + fi |
63 | +} |
64 | \ No newline at end of file |
65 | |
66 | === added directory 'lib/offspring/build/tests' |
67 | === added file 'lib/offspring/build/tests/offspring_test_key' |
68 | --- lib/offspring/build/tests/offspring_test_key 1970-01-01 00:00:00 +0000 |
69 | +++ lib/offspring/build/tests/offspring_test_key 2011-10-06 17:54:25 +0000 |
70 | @@ -0,0 +1,27 @@ |
71 | +-----BEGIN RSA PRIVATE KEY----- |
72 | +MIIEowIBAAKCAQEAuzjspIVMkDrI+1MkDCFVh9XBbkc55hCpI/RqMz594jDc86iM |
73 | +4vRmq5R+HJszi/mbbklgsD8x0bGMz65u+yvp9AxScn0/d5GXfhZKHz3fROjT4WEf |
74 | +1NB2gqAink3e9YJZ+04sAJ1Xz81w8wkoTh7roU35GBXSjbH1rMl17qLny8pUsuv6 |
75 | +gehemzYAcvM9mAASfAzNvPR87NjrbizYMOwWsQ6stEm4Aemdcio42ZZBHXVm/QV0 |
76 | +hOTXLfopOH622iIuEFU5j1blUY/NXyDBU+hNUmMeO9DpL06oDu0XIdwb3nPaEQ3n |
77 | +U+I1VvTR8gz1sQgXYVGa3qGbAm01mcXWQ+Is8QIDAQABAoIBADFRnYT5WGHmGmua |
78 | +SzSm01ElDf9u4+GnIedGy3MUUzTyikHldLeUijdItq/ycnG9HyS+T6od+5Gxo9ZR |
79 | +rQqdVtPjKxTdyYpF4BJm7L+uHNKaQrZsT2ZQQ+fFJ1lsSf+ChxGcVhsTV7517/sV |
80 | +vnhVzNyBHc0qcnzBFGaf62EhqM4V2ogsj3bLd6nwRAyvpbUMrWWv057teKooxBsk |
81 | +j0QXP3WYEzAmdbphkG1qPCGJQNhYBbyjU7Cf4bJg/CbHENynM8zlbiFVUWgfwtNJ |
82 | +ZcddrFs3Y1P6nDa/Jd2ifqx6V7M7LAqpKjqwiuCNPiXVWyxmELaKu8Yvmqsv5qXF |
83 | +ixKcDAECgYEA9Ozj7r0PHkrrCcoGdIQTs1sZS0eV+yDKdOOlUeFpPMHi6PqROOwY |
84 | +cKr4RtkJC/AgjfA3tUEBemVKYoCOVWYeHmlTd3wQpl7lqqGN431A18/Z0psKpFh+ |
85 | +wLfMZXBslYybY3b+VguICuyICN/hksCxKJNurKalPow/EYqo0dspOkkCgYEAw7AZ |
86 | +QKCfnuYfGdGto8JFizkxS6uvTInXNTzRZPdkbgd/JhCmnXENg93QSo/GWe2dehQG |
87 | +NZkCpVnZFKkubbxCc/OBAF+es7dVRc8vy8uwa2nOW9SG6yLNlNKdTwJxmRlV+53H |
88 | +mzC0PZ4GyMnBFJ46+rem3siLLVMlkolbZzlnHWkCgYA8+e0VNsRYylYRrdZFk8xD |
89 | +zt5RO5U/XD6LM1GpPPEySyLu1dLp1P2Qrz/4g3gZHMM+ExwLaA+yJR2LwG2vHSlK |
90 | +cPZyvNR4Vw/elzH3/Orzz69vG2Je4BlOaXPdnUurP8I/1RQk3+IStih37ST/oDF6 |
91 | +5JmdKi/hjpD1EQxOkr2E4QKBgC9Y/3MsqhJ3WZUUr6/MxKjgCLZnbv3U6DZgZcXJ |
92 | +OgqJU9Fw++9iOEPsuoYf7X06yfyMtcfoIsTBTY37NVml0Gpfw5nEiRCwzjga3lSw |
93 | +DxqeOijr7k0cWaOlphxE2hmSEMTVs0MwcJvsDXYtosMLWffp0b1bxpkL4i5nf68l |
94 | +K3bpAoGBAJR5uzQGl5pXg7QKYPv1Sdi6pBp1wGSpFzzPatlpnrO8zZi9c0O29h7R |
95 | +3lMfBazjjNQmJF25uh+svgPCboNDh9dhqDBKf/ep0ju4wpXF2I0JM5xhYP2s242K |
96 | +mqsBXi4JWa9GAjVqVA48M3Vsu4r1YTOxIqDjnAFpnTU2tzzp2OFZ |
97 | +-----END RSA PRIVATE KEY----- |
98 | |
99 | === added file 'lib/offspring/build/tests/offspring_test_key.pub' |
100 | --- lib/offspring/build/tests/offspring_test_key.pub 1970-01-01 00:00:00 +0000 |
101 | +++ lib/offspring/build/tests/offspring_test_key.pub 2011-10-06 17:54:25 +0000 |
102 | @@ -0,0 +1,1 @@ |
103 | +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7OOykhUyQOsj7UyQMIVWH1cFuRznmEKkj9GozPn3iMNzzqIzi9GarlH4cmzOL+ZtuSWCwPzHRsYzPrm77K+n0DFJyfT93kZd+FkofPd9E6NPhYR/U0HaCoCKeTd71gln7TiwAnVfPzXDzCShOHuuhTfkYFdKNsfWsyXXuoufLylSy6/qB6F6bNgBy8z2YABJ8DM289Hzs2OtuLNgw7BaxDqy0SbgB6Z1yKjjZlkEddWb9BXSE5Nct+ik4frbaIi4QVTmPVuVRj81fIMFT6E1SYx470OkvTqgO7Rch3Bvec9oRDedT4jVW9NHyDPWxCBdhUZreoZsCbTWZxdZD4izx dooferlad@JT-Linaro-ThinkPad-T410 |
104 | |
105 | === added file 'lib/offspring/build/tests/test.sh' |
106 | --- lib/offspring/build/tests/test.sh 1970-01-01 00:00:00 +0000 |
107 | +++ lib/offspring/build/tests/test.sh 2011-10-06 17:54:25 +0000 |
108 | @@ -0,0 +1,24 @@ |
109 | +#!/bin/bash |
110 | + |
111 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
112 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
113 | + |
114 | +# Get the full directory path of the test script |
115 | +DIR="$( cd "$( dirname "$0" )" && pwd )" |
116 | + |
117 | +# Load functions under test |
118 | +source "${DIR}/../functions/ssh.sh" |
119 | + |
120 | +# Load test key |
121 | +AddKeys "${DIR}/offspring_test_key" |
122 | + |
123 | +# See what keys are loaded (expect lib/offspring/build/tests/offspring_test_key) |
124 | +echo After loading test key, keys are: |
125 | +ssh-add -l |
126 | + |
127 | +# Stop the loaded ssh-agent process |
128 | +StopSSHAgent |
129 | + |
130 | +# Check that no keys are listed: Expect "Could not open a connection to your authentication agent." |
131 | +echo "After killing ssh agent, we shouldn't be able to find any keys..." |
132 | +ssh-add -l |
133 | |
134 | === added file 'lib/offspring/build/wrappers/test.sh' |
135 | --- lib/offspring/build/wrappers/test.sh 1970-01-01 00:00:00 +0000 |
136 | +++ lib/offspring/build/wrappers/test.sh 2011-10-06 17:54:25 +0000 |
137 | @@ -0,0 +1,52 @@ |
138 | +#!/bin/bash |
139 | + |
140 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
141 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
142 | + |
143 | +Buildtool_setup() { |
144 | + Catch_faults permit_errors |
145 | + |
146 | + TARGETFS="${IBS_WORKDIR}/${PROJECT}/chroot" |
147 | + |
148 | + if [ -d "${IBS_WORKDIR}/${PROJECT}/config" ]; then |
149 | + rm -rf "${IBS_WORKDIR}/${PROJECT}/config" > /dev/null 2>&1 || true |
150 | + fi |
151 | + |
152 | + Action "Copying project configuration directory into work directory for build" |
153 | + Export_project_config "${PROJECT}" "${IBS_WORKDIR}/${PROJECT}/config" |
154 | + Action_done |
155 | + |
156 | + if [ "$?" != "0" ]; then |
157 | + Error "Unable to copy project build configuration to %s" "${IBS_WORKDIR}/${PROJECT}/config" |
158 | + Die |
159 | + fi |
160 | + Catch_faults disallow_errors |
161 | +} |
162 | + |
163 | +Buildtool_build() { |
164 | + Action "Test Build - Buildtool_build" |
165 | + Action_done |
166 | +} |
167 | + |
168 | +Buildtool_cleanup() { |
169 | + Action "Test Build - Buildtool_cleanup" |
170 | + Action_done |
171 | +} |
172 | + |
173 | +_Check_directory() { |
174 | + if [ "${PWD}" != "${IBS_WORKDIR}/${PROJECT}" ]; then |
175 | + Error "Working directory is '%s'; expected '%s'" "{$PWD}" "${IBS_WORKDIR}/${PROJECT}" |
176 | + Die |
177 | + fi |
178 | + Debug "Directory check performed and was successful; PWD: %s" "${PWD}" |
179 | +} |
180 | + |
181 | +_SaveConfig() { |
182 | + Action "Test Build - _SaveConfig" |
183 | + Action_done |
184 | +} |
185 | + |
186 | +_SaveBuildWorkDirectory() { |
187 | + Action "Test Build - _SaveBuildWorkDirectory" |
188 | + Action_done |
189 | +} |
190 | |
191 | === modified file 'lib/offspring/master/models.py' |
192 | --- lib/offspring/master/models.py 2011-09-23 14:01:26 +0000 |
193 | +++ lib/offspring/master/models.py 2011-10-06 17:54:25 +0000 |
194 | @@ -108,7 +108,10 @@ |
195 | return (self.previous_state, self.current_state) |
196 | |
197 | def build(self, request): |
198 | - if self.server_proxy.startBuild(request.project.name, request.project.config_url) == LexbuilderSlaveServer.REQUEST_OKAY: |
199 | + if self.server_proxy.startBuild(request.project.name, |
200 | + request.project.config_url, |
201 | + request.project.lp_user, |
202 | + request.project.lp_ssh_key) == LexbuilderSlaveServer.REQUEST_OKAY: |
203 | self.previous_state = self.current_state |
204 | self.current_state = LexbuilderSlave.STATE_BUILDING |
205 | self.current_job = BuildResult(request, self) |
206 | @@ -165,7 +168,13 @@ |
207 | status = Unicode() |
208 | config_url = Unicode() |
209 | notes = Unicode() |
210 | - |
211 | + |
212 | + # The Launchpad User and SSH key are stored per project. If we stored them |
213 | + # per LauncpadProject, anyone who could create a Project referencing that |
214 | + # LaunchpadProject could get access to the private data in it. |
215 | + lp_user = Unicode() |
216 | + lp_ssh_key = Unicode() |
217 | + |
218 | def __init__(self, name, priority=40): |
219 | self.title = unicode(name.capitalize()) |
220 | self.name = unicode(name) |
221 | |
222 | === modified file 'lib/offspring/slave/slave.py' |
223 | --- lib/offspring/slave/slave.py 2011-09-23 14:01:26 +0000 |
224 | +++ lib/offspring/slave/slave.py 2011-10-06 17:54:25 +0000 |
225 | @@ -1,6 +1,7 @@ |
226 | # Copyright 2010 Canonical Ltd. This software is licensed under the |
227 | # GNU Affero General Public License version 3 (see the file LICENSE). |
228 | |
229 | + |
230 | __all__ = ( |
231 | 'LexbuilderSlave', |
232 | 'LexbuilderSlaveServer', |
233 | @@ -12,6 +13,8 @@ |
234 | import platform |
235 | import time |
236 | import xmlrpclib |
237 | +import re |
238 | +import tempfile |
239 | |
240 | from offspring import config, offspring_root |
241 | from offspring.daemon import ( |
242 | @@ -57,9 +60,30 @@ |
243 | self.server.stop() |
244 | self.server.join() |
245 | |
246 | - def startBuild(self, projectName, configBranchURL): |
247 | + def add_user_to_bzr_ssh_url(self, configBranchURL, lpUser): |
248 | + # check for lp: format URLs. Convert these to bzr+ssh ones |
249 | + configBranchURL = re.sub("lp:", "bzr+ssh://bazaar.launchpad.net/", |
250 | + configBranchURL) |
251 | + |
252 | + # The assumption is that this function will only be called with a |
253 | + # bzr+ssh URL (hence the name!) |
254 | + assert(re.search("bzr\+ssh://", configBranchURL)) |
255 | + |
256 | + # Add user to URL |
257 | + configBranchURL = re.sub("bzr\+ssh://", |
258 | + "bzr+ssh://" + lpUser + "@", configBranchURL) |
259 | + |
260 | + return configBranchURL |
261 | + |
262 | + def startBuild(self, projectName, configBranchURL, lpUser=None, lpSSHKey=None): |
263 | + |
264 | + if lpUser: |
265 | + # We don't want to pass the user separately, we want it to be |
266 | + # integrated into the configBranchURL. |
267 | + configBranchURL = self.add_user_to_bzr_ssh_url(configBranchURL, lpUser) |
268 | + |
269 | self.currentBuild = ProjectBuildProcess( |
270 | - self, projectName, configBranchURL) |
271 | + self, projectName, configBranchURL, lpSSHKey) |
272 | logging.info("Starting build: %s %s %s" % ( |
273 | self.currentBuild.getCommandPath(), projectName, configBranchURL)) |
274 | self.currentBuild.start() |
275 | @@ -70,9 +94,12 @@ |
276 | if self.currentBuild.process is not None: |
277 | if self.currentBuild.process.poll() is None: |
278 | self.currentBuild.process.terminate() |
279 | - # Give it 5 seconds before sending kill. |
280 | - time.sleep(int(config.slave("child_wait_timeout"))) |
281 | - self.currentBuild.process.kill() |
282 | + if self.currentBuild.process.poll() is None: |
283 | + # Give it 5 seconds before sending kill. |
284 | + time.sleep(int(config.slave("child_wait_timeout"))) |
285 | + if self.currentBuild.process.poll() is None: |
286 | + self.currentBuild.process.kill() |
287 | + self.currentBuild.tidyUp() |
288 | |
289 | |
290 | class LexbuilderSlaveServer(LexbuilderXMLRPCServer): |
291 | @@ -90,11 +117,11 @@ |
292 | LexbuilderXMLRPCServer.__init__( |
293 | self, addr, requestHandler, logRequests, allow_none, encoding, request_timeout) |
294 | |
295 | - def api_startBuild(self, projectName, configBranchURL): |
296 | + def api_startBuild(self, projectName, configBranchURL, lpUser=None, lpSSHKey=None): |
297 | if self.slave.currentBuild is not None: |
298 | if self.slave.currentBuild.state == ProjectBuildProcess.STATE_ACTIVE: |
299 | return LexbuilderSlaveServer.REQUEST_ERROR |
300 | - self.slave.startBuild(projectName, configBranchURL) |
301 | + self.slave.startBuild(projectName, configBranchURL, lpUser, lpSSHKey) |
302 | return LexbuilderSlaveServer.REQUEST_OKAY |
303 | |
304 | def api_stopBuild(self): |
305 | @@ -159,14 +186,34 @@ |
306 | |
307 | class ProjectBuildProcess(MonitoredProcess): |
308 | |
309 | - def __init__(self, supervisor, projectName, configBranchURL): |
310 | - MonitoredProcess.__init__( |
311 | - self, supervisor, [projectName, configBranchURL], |
312 | + def __init__(self, supervisor, projectName, configBranchURL, lpSSHKey=None): |
313 | + args = [projectName, configBranchURL] |
314 | + self.ssh_key_file = None |
315 | + |
316 | + if lpSSHKey: |
317 | + # Write SSH key to disk. tempfile will delete it after the build |
318 | + # when this class goes out of scope. |
319 | + # Need to have this here so we can pass the temp file name |
320 | + # containing the key to the builder process on its command line. |
321 | + self.ssh_key_file = tempfile.NamedTemporaryFile() |
322 | + args.append(self.ssh_key_file.name) |
323 | + self.ssh_key_file.write(lpSSHKey) |
324 | + self.ssh_key_file.flush() |
325 | + |
326 | + MonitoredProcess.__init__(self, supervisor, args, |
327 | environment_updates={ |
328 | 'CONFIG_SCRIPT': os.path.join( |
329 | offspring_root, 'bin', 'offspring-builder-config')}) |
330 | self.projectName = projectName |
331 | |
332 | + def __del__(self): |
333 | + self.tidyUp() |
334 | + |
335 | + def tidyUp(self): |
336 | + if self.ssh_key_file: |
337 | + self.ssh_key_file.close() |
338 | + self.ssh_key_file = None |
339 | + |
340 | def setup(self): |
341 | self.buildName = None |
342 | |
343 | @@ -189,6 +236,7 @@ |
344 | logging.debug( |
345 | "Setting state of slave to %s" % LexbuilderSlave.STATE_IDLE) |
346 | self.master.state = LexbuilderSlave.STATE_IDLE |
347 | + self.tidyUp() |
348 | |
349 | def getCommandPath(self): |
350 | return config.slave("build_command") |
351 | |
352 | === added directory 'lib/offspring/slave/tests' |
353 | === added file 'lib/offspring/slave/tests/test_private_ssh_keys.py' |
354 | --- lib/offspring/slave/tests/test_private_ssh_keys.py 1970-01-01 00:00:00 +0000 |
355 | +++ lib/offspring/slave/tests/test_private_ssh_keys.py 2011-10-06 17:54:25 +0000 |
356 | @@ -0,0 +1,110 @@ |
357 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
358 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
359 | + |
360 | +import os |
361 | +import subprocess |
362 | +import offspring.slave |
363 | +import unittest |
364 | +import xmlrpclib |
365 | +import re |
366 | +import os.path |
367 | + |
368 | + |
369 | +class TestLexbuilderSlavePrivateSSHKeys(unittest.TestCase): |
370 | + |
371 | + def nothing(self): |
372 | + pass |
373 | + |
374 | + def setUp(self): |
375 | + self.slave = offspring.slave.LexbuilderSlave(('localhost', 8760)) |
376 | + self.slave.start() |
377 | + self.x = xmlrpclib.ServerProxy("http://admin:password@localhost:8760") |
378 | + self.PBP_start = offspring.slave.slave.ProjectBuildProcess.start |
379 | + |
380 | + def tearDown(self): |
381 | + offspring.slave.slave.ProjectBuildProcess.start = self.PBP_start |
382 | + self.slave.stop() |
383 | + |
384 | + def test_startBuildWithCustomSSHKey(self): |
385 | + # Test doesn't want to trigger an actual build. The following line |
386 | + # prevents this. |
387 | + offspring.slave.slave.ProjectBuildProcess.start = self.nothing |
388 | + |
389 | + args = ["project_name", "lp:~dooferlad/offspring/config", "user", "key"] |
390 | + |
391 | + self.x.startBuild(args[0], args[1], args[2], args[3]) |
392 | + |
393 | + passed_args = self.slave.currentBuild.arguments |
394 | + |
395 | + bzr_path = re.sub("lp:", |
396 | + "bzr+ssh://" + args[2] + "@bazaar.launchpad.net/", |
397 | + args[1]) |
398 | + |
399 | + expected_args = [args[0], bzr_path, |
400 | + self.slave.currentBuild.ssh_key_file.name] |
401 | + |
402 | + self.assertEqual(expected_args, passed_args) |
403 | + |
404 | + def test_checkSSHKeyDeletedWhenBuildFinished(self): |
405 | + # Test doesn't want to trigger an actual build. The following line |
406 | + # prevents this. |
407 | + offspring.slave.slave.ProjectBuildProcess.start = self.nothing |
408 | + |
409 | + args = ["project_name", "lp:~dooferlad/offspring/config", "user", "key"] |
410 | + |
411 | + self.x.startBuild(args[0], args[1], args[2], args[3]) |
412 | + |
413 | + file_path = self.slave.currentBuild.ssh_key_file.name |
414 | + read_key = open(file_path).read() |
415 | + |
416 | + # We should have a key written to a file for the slave to read... |
417 | + self.assertEqual(read_key, args[3]) |
418 | + |
419 | + # Can't use XMLRPC interface here because it checks to see if a build |
420 | + # has actually started. We just want to test the clean up logic. |
421 | + self.slave.stopBuild() |
422 | + # Having stopped the slave, the key should have been erased |
423 | + self.assertFalse(os.path.exists(file_path)) |
424 | + |
425 | + def test_checkSSHAgent(self): |
426 | + # This is a wrapper around a shell script that runs the commands and |
427 | + # we just check the output. |
428 | + |
429 | + dir_of_this_file = os.path.dirname(os.path.abspath(__file__)) |
430 | + test_script = os.path.join(dir_of_this_file, "../../build/tests/test.sh") |
431 | + |
432 | + p = subprocess.Popen(test_script, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
433 | + |
434 | + stdout = p.communicate()[0] |
435 | + |
436 | + lines = stdout.split("\n") |
437 | + |
438 | + # Sample output: |
439 | + # Agent pid 9839 |
440 | + # Identity added: /offspring/lib/offspring/build/tests/offspring_test_key (/offspring/lib/offspring/build/tests/offspring_test_key) |
441 | + # After loading test key, keys are: |
442 | + # 2048 fe:47:60:de:1a:1f:39:de:1d:91:43:d1:88:97:36:9b /offspring/lib/offspring/build/tests/offspring_test_key (RSA) |
443 | + # After killing ssh agent, we shouldn't be able to find any keys... |
444 | + # Could not open a connection to your authentication agent. |
445 | + |
446 | + # Expect the first line to be of the form "Agent pid [PID]" |
447 | + self.assertTrue(re.search("Agent pid \d+", lines[0]), |
448 | + "Test build ssh-agent check failed: Expected to find "+ |
449 | + "Agent pid <PID>, found:\n" + lines[0]) |
450 | + |
451 | + # Expect the second line to be of the form Identity added: <path to...>/offspring_test_key |
452 | + self.assertTrue(re.search("Identity added:.*?offspring_test_key", lines[1]), |
453 | + "Test build ssh-agent check failed: Expected to find "+ |
454 | + "Identity added: <path to...>/offspring_test_key, found:\n" + lines[1]) |
455 | + |
456 | + # Ignore the third line, it is just a fixed message |
457 | + # Expect the fourth line to be <key fingerprint> /path/to/offspring_test_key (RSA) |
458 | + self.assertTrue(re.search("offspring_test_key \(RSA\)", lines[3]), |
459 | + "Test build ssh-agent check failed: Expected to find "+ |
460 | + "offspring_test_key (RSA), found:\n" + lines[3]) |
461 | + |
462 | + # Ignore the fifth line, it is just a fixed message |
463 | + # Expect the sixth line to be "Could not open a connection to your authentication agent." |
464 | + self.assertTrue(re.search("^Could not open a connection to your authentication agent\.$", lines[5]), |
465 | + "Test build ssh-agent check failed: Expected to find "+ |
466 | + "Could not open a connection to your authentication agent, found:\n" + lines[5]) |
467 | \ No newline at end of file |
468 | |
469 | === renamed file 'lib/offspring/slave/tests.py' => 'lib/offspring/slave/tests/tests.py' |
470 | === modified file 'lib/offspring/web/queuemanager/admin.py' |
471 | --- lib/offspring/web/queuemanager/admin.py 2011-02-24 05:08:42 +0000 |
472 | +++ lib/offspring/web/queuemanager/admin.py 2011-10-06 17:54:25 +0000 |
473 | @@ -2,6 +2,7 @@ |
474 | # GNU Affero General Public License version 3 (see the file LICENSE). |
475 | |
476 | from django.contrib import admin |
477 | +from django import forms |
478 | |
479 | from offspring.web.queuemanager.forms import ProjectBaseForm |
480 | from offspring.web.queuemanager.models import ( |
481 | @@ -43,7 +44,8 @@ |
482 | |
483 | |
484 | class ProjectAdmin(admin.ModelAdmin): |
485 | - fields = ['title', 'name', 'arch', 'project_group', 'launchpad_project', 'suite', 'series', 'priority', 'status', 'is_active', 'config_url', 'notes'] |
486 | + fields = ['title', 'name', 'arch', 'project_group', 'launchpad_project', 'suite', 'series', 'priority', |
487 | + 'status', 'is_active','config_url', 'lp_user', 'lp_ssh_key_input', 'notes'] |
488 | list_display = ('display_name', 'arch', 'series', 'project_group', 'launchpad_project', 'is_active', 'status', 'priority', 'config_url') |
489 | list_filter = ['arch', 'series', 'is_active', 'project_group', 'status'] |
490 | search_fields = ['title', 'name', 'arch', 'notes'] |
491 | |
492 | === modified file 'lib/offspring/web/queuemanager/forms.py' |
493 | --- lib/offspring/web/queuemanager/forms.py 2011-03-03 00:06:58 +0000 |
494 | +++ lib/offspring/web/queuemanager/forms.py 2011-10-06 17:54:25 +0000 |
495 | @@ -4,6 +4,7 @@ |
496 | from django.db import models |
497 | from django.forms import ModelChoiceField, ModelForm, Textarea, TextInput |
498 | from django.forms import fields |
499 | +from django import forms |
500 | |
501 | from offspring.web.queuemanager.models import ( |
502 | Project, |
503 | @@ -12,11 +13,87 @@ |
504 | ) |
505 | |
506 | from offspring.web.queuemanager.widgets import SelectWithAddNew |
507 | +import re |
508 | + |
509 | |
510 | class ProjectBaseForm(ModelForm): |
511 | status = fields.CharField(max_length=200, |
512 | widget=fields.Select(choices=Project.STATUS_CHOICES), required=True) |
513 | |
514 | + ssh_help = ("Enter a private SSH ASCII key block, complete with begin "+ |
515 | + "and end markers. Saved keys are not shown. If you wish to "+ |
516 | + "erase a saved key (rather than replacing it) enter the "+ |
517 | + "string \"ERASE\".") |
518 | + |
519 | + lp_ssh_key_input = forms.CharField(label="Launchpad User's SSH key", |
520 | + required=False, |
521 | + widget=forms.Textarea( |
522 | + attrs={'cols': 73, 'rows' : 4}), |
523 | + help_text=ssh_help) |
524 | + |
525 | + def __init__(self, *args, **kwargs): |
526 | + super(ProjectBaseForm, self).__init__(*args, **kwargs) |
527 | + |
528 | + # Make sure lp_ssh_key_input turns up next to lp_user. This is needed |
529 | + # because I insert a special lp_ssh_key_input field in this class |
530 | + # (above) and without doing this it will show up last. |
531 | + field_order = self.fields.keyOrder |
532 | + field_order.pop(field_order.index('lp_ssh_key_input')) |
533 | + lp_user_pos = field_order.index('lp_user') |
534 | + field_order.insert(lp_user_pos + 1, 'lp_ssh_key_input') |
535 | + |
536 | + self.ui_messages = {"ssh set": |
537 | + "An SSH key has already been saved for this "+ |
538 | + "project. To update it paste a new one here.", |
539 | + "ssh clear": |
540 | + "No SSH key has been saved for this project. "+ |
541 | + "Paste one here to set."} |
542 | + |
543 | + # Set the form fields based on the model object |
544 | + if kwargs.has_key('instance'): |
545 | + instance = kwargs['instance'] |
546 | + if instance.lp_ssh_key and instance.lp_ssh_key != "": |
547 | + self.initial['lp_ssh_key_input'] = self.ui_messages["ssh set"] |
548 | + else: |
549 | + self.initial['lp_ssh_key_input'] = self.ui_messages["ssh clear"] |
550 | + |
551 | + def clean_lp_ssh_key_input(self): |
552 | + new_key = self.cleaned_data['lp_ssh_key_input'] |
553 | + |
554 | + key_search_regexp = (r"-----BEGIN \w+ PRIVATE KEY-----"+ |
555 | + r".*"+ |
556 | + r"-----END \w+ PRIVATE KEY-----") |
557 | + is_key = re.search(key_search_regexp, new_key, re.DOTALL | re.MULTILINE) |
558 | + is_message = new_key in self.ui_messages.values() |
559 | + |
560 | + if not(is_key or new_key == "" or is_message or new_key == "ERASE"): |
561 | + raise forms.ValidationError( |
562 | + "Key block doesn't look valid - not saved.") |
563 | + |
564 | + # Always return the cleaned data, whether you have changed it or not. |
565 | + return new_key |
566 | + |
567 | + def save(self, commit=True): |
568 | + model = super(ProjectBaseForm, self).save(commit=False) |
569 | + |
570 | + # Save the SSH key |
571 | + new_key = self.cleaned_data['lp_ssh_key_input'] |
572 | + is_message = new_key in self.ui_messages.values() |
573 | + |
574 | + if new_key == "" or is_message: |
575 | + pass # No new key entered |
576 | + |
577 | + elif new_key == "ERASE": |
578 | + model.lp_ssh_key = "" # Erase stored key |
579 | + |
580 | + else: # Save new key (validated in clean_lp_ssh_key_input) |
581 | + model.lp_ssh_key = new_key |
582 | + |
583 | + if commit: |
584 | + model.save() |
585 | + |
586 | + return model |
587 | + |
588 | class Meta: |
589 | model = Project |
590 | widgets = { |
591 | @@ -24,6 +101,7 @@ |
592 | 'series' : TextInput(attrs={'style': 'text-transform: lowercase;'}), |
593 | 'config_url': TextInput(attrs={'size': 50}), |
594 | 'notes' : Textarea(attrs={'cols': 73, 'rows' : 4}), |
595 | + 'lp_ssh_key': Textarea(attrs={'cols': 73, 'rows' : 4}), |
596 | } |
597 | |
598 | def clean_name(self): |
599 | |
600 | === modified file 'lib/offspring/web/queuemanager/models.py' |
601 | --- lib/offspring/web/queuemanager/models.py 2011-09-28 14:23:09 +0000 |
602 | +++ lib/offspring/web/queuemanager/models.py 2011-10-06 17:54:25 +0000 |
603 | @@ -134,6 +134,13 @@ |
604 | launchpad_project = models.ForeignKey("LaunchpadProject", blank=True, null=True) |
605 | status = models.CharField('status', max_length=200, null=True, choices=STATUS_CHOICES) |
606 | config_url = models.CharField('config URL', max_length=200) # Add some kind of validator here. |
607 | + |
608 | + # The Launchpad User and SSH key are stored per project. If we stored them |
609 | + # per LauncpadProject, anyone who could create a Project referencing that |
610 | + # LaunchpadProject could get access to the private data in it. |
611 | + lp_user = models.CharField('Launchpad User', max_length=30) |
612 | + lp_ssh_key = models.TextField("!!!", blank=True, null=True, editable=False) |
613 | + |
614 | notes = models.TextField(blank=True, null=True) |
615 | |
616 | class Meta: |
Hi James,
On Thu, 2011-09-29 at 17:39 +0000, James Tunnicliffe wrote:
> Same as yesterday (nearly), now targeting the correct branch.
>
> I have an SQL migration script ready, but I will submit it on top of salgado's change that introduces the migration directory.
>
> This change adds support for adding an SSH user and private key to an Offspring project on creation. The key is written to a temporary file, the location of which is passed to the builder, which then loads it into a new instance of ssh-agent, which is killed after the build is finished. At this point the key file is deleted.
>
> The SSH user is inserted into the bzr branch URL. lp: URLs are supported, as are bzr+ssh.
Did you consider calling "bzr lp-login <username>" right after running build/bin/ offspring- build' build/bin/ offspring- build 2011-09-21 21:06:01 +0000 build/bin/ offspring- build 2011-09-29 17:38:30 +0000
ssh-add instead of changing the branch URL when starting the build?
That would be simpler to implement and more flexible as it'd allow any
URL schemes, but it'd also allow the build script to fetch other
branches from LP using the same user.
>
> As yet I have not modified the admin interface. You can add a project through the non-admin UI and from there add a user + SSH key. On the admin interface you can't see the key (which has to be not pass phrase protected) or user name. I am planning on adding a sensible way of updating the user and key without leaking the key back out of the interface tomorrow, at which point I think this feature will be complete.
> differences between files attachment (review-diff.txt)
> === modified file 'lib/offspring/
> --- lib/offspring/
> +++ lib/offspring/
> @@ -168,10 +168,39 @@
> Stop_logging
> Catch_faults disable
>
> + if [ $RUN_SSH_AGENT = 1 ]; then
> + echo kill
Was this just for debugging?
> + kill $SSH_AGENT_PID
> + fi
Given the security implications of failing to kill the ssh agent, I
think it's really important that we have a test for this. Do you have
any idea how hard it'd be to write one?
> + $INSERT_ POINT]= "$ARG" POINT=$ ((INSERT_ POINT+1) )
> Print
> Print "OK: %s build %s completed at %s." "${PROJECT}" "${BUILDNAME}" "`date`"
> }
>
> +_AddKeys() {
> + echo AddKeys
> + INDEX=0
> + RUN_SSH_AGENT=0
> + SSH_KEYS[0]=0
> + INSERT_POINT=0
> + for ARG in "$@"
> + do
> + if [ $INDEX -gt 2 ]; then # key files...
> + let RUN_SSH_AGENT=1
> + SSH_KEYS[
> + INSERT_
Would it be possible to simplify this a bit by checking the number of
arguments?
if [ $# -gt 2 ]; then
# We were passed an SSH key to use when fetching the config from LP
SSH_KEY=$3
fi
And later you could even use SSH_KEY to decide whether or not run run
ssh-agent?
> + fi
> + done
> +
> + if [ $RUN_SSH_AGENT = 1 ]; then
> + eval `ssh-agent`
> + for KEY in "$SSH_KEYS"
The for loop is not needed here as there can only be one SSH key
associated with a project, right?
> + do ####### ####### ####### ## ####### ####### ####### ##
> + ssh-add $KEY
> + done
> + fi
> +}
> +
> #######
> # Main
> #######
> @@ -19...