Merge lp:~dooferlad/offspring/linaro_offspring_add_bzr_ssh into lp:~linaro-automation/offspring/linaro

Proposed by James Tunnicliffe
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
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.

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.

To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal
Download full text (12.9 KiB)

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
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/build/bin/offspring-build'
> --- lib/offspring/build/bin/offspring-build 2011-09-21 21:06:01 +0000
> +++ lib/offspring/build/bin/offspring-build 2011-09-29 17:38:30 +0000
> @@ -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?

> +
> 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_POINT]="$ARG"
> + INSERT_POINT=$((INSERT_POINT+1))

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

Revision history for this message
James Tunnicliffe (dooferlad) wrote : Posted in a previous version of this proposal
Download full text (9.5 KiB)

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/authentication.conf to store a block of
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://doc.bazaar.canonical.com/latest/en/user-reference/configuration-help.html
http://doc.bazaar.canonical.com/developers/authentication-ring.html
http://doc.bazaar.canonical.com/latest/en/user-reference/authentication-help.html

A quick skim of these says that all URL formats accept the form:
  bzr branch <scheme>://<user>@host/path
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/build/bin/offspring-build'
>> --- lib/offspring/build/bin/offspring-build   2011-09-21 21:06:01 +0000
>> +++ lib/offspring/build/bin/offspring-build   2011-09-29 17:38:30 +0000
>> @@ -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...

Read more...

Revision history for this message
Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal
Download full text (5.2 KiB)

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/authentication.conf to store a block of
> 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://doc.bazaar.canonical.com/latest/en/user-reference/configuration-help.html
> http://doc.bazaar.canonical.com/developers/authentication-ring.html
> http://doc.bazaar.canonical.com/latest/en/user-reference/authentication-help.html
>
> A quick skim of these says that all URL formats accept the form:
> bzr branch <scheme>://<user>@host/path
> 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/build/bin/offspring-build'
> >> --- lib/offspring/build/bin/offspring-build 2011-09-21 21:06:01 +0000
> >> +++ lib/offspring/build/bin/offspring-build 2011-09-29 17:38:30 +0000
> >> @@ -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!
> ...

Read more...

Revision history for this message
James Tunnicliffe (dooferlad) wrote : Posted in a previous version of this proposal
Download full text (5.8 KiB)

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/authentication.conf to store a block of
>> 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://doc.bazaar.canonical.com/latest/en/user-reference/configuration-help.html
>> http://doc.bazaar.canonical.com/developers/authentication-ring.html
>> http://doc.bazaar.canonical.com/latest/en/user-reference/authentication-help.html
>>
>> A quick skim of these says that all URL formats accept the form:
>>   bzr branch <scheme>://<user>@host/path
>> 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/build/bin/offspring-build'
>> >> --- lib/offspring/build/bin/offspring-buil...

Read more...

Revision history for this message
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-hwpack-create to run something else.

If this is used for building images with live-build, then that is
trivially possible.

Thanks,

James

Revision history for this message
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-hwpack-create to run something else.
>
> 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

Revision history for this message
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-group-access, which likely isn't installed.

> >> 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-hwpack-create to run something else.
> >
> > 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

Revision history for this message
Guilherme Salgado (salgado) wrote :
Download full text (22.5 KiB)

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-infrastructure)
>
> For more details, see:
> https://code.launchpad.net/~dooferlad/offspring/linaro_offspring_add_bzr_ssh/+merge/78279
>
> 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/build/bin/offspring-build'
> --- lib/offspring/build/bin/offspring-build 2011-09-21 21:06:01 +0000
> +++ lib/offspring/build/bin/offspring-build 2011-10-05 16:24:23 +0000
> @@ -35,6 +35,7 @@
> source "${IBS_FUNCDIR}/reports.sh"
> source "${IBS_FUNCDIR}/logging.sh"
> source "${IBS_FUNCDIR}/files.sh"
> + source "${IBS_FUNCDIR}/ssh.sh"
>
> [ -n "${IBS_PROXY}" ] && export http_proxy="${IBS_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/build/functions/exit.sh'
> --- lib/offspring/build/functions/exit.sh 2010-11-29 08:27:24 +0000
> +++ lib/offspring/build/functions/exit.sh 2011-10-05 16:24:23 +0000
> @@ -61,6 +61,9 @@
> Action_done
> fi
>
> + # Always stop SSH agent...
> + StopSSHAgent
> +
> Action "Remvoving build statefile"
> rm ${IBS_BUILDER_STATEFILE}
> Action_done
>
> === added file 'lib/offspring/build/functions/ssh.sh'
> --- lib/offspring/build/functions/ssh.sh 1970-01-01 00:00:00 +0000
> +++ lib/offspring/build/functions/ssh.sh 2011-10-05 16:24:23 +0000
> @@ -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
> + ...

Revision history for this message
James Tunnicliffe (dooferlad) wrote :
Download full text (8.9 KiB)

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/slave/slave.py'
>> -    def startBuild(self, projectName, configBranchURL):
>> +    def add_user_to_bzr_ssh_url(self, configBranchURL, lpUser):
>> +        # check for lp: format URLs. Convert these to bzr+ssh ones
>> +        configBranchURL = re.sub("lp:", "bzr+ssh://bazaar.launchpad.net/",
>> +                                 configBranchURL)
>
> I thought you were going to use 'bzr lp-login' as it's not a problem if
> we fail to cleanup ~/.bazaar/authentication.conf as long as we make sure
> 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/authentication.conf to the build script
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_user_to_bzr_ssh_url(configBranchURL, lpUser)
>> +
>>          self.currentBuild = ProjectBuildProcess(
>> -            self, projectName, configBranchURL)
>> +            self, projectName, configBranchURL, lpSSHKey)
>>          logging.info("Starting build: %s %s %s" % (
>>              self.currentBuild.getCommandPath(), projectName, configBranchURL))
>>          self.currentBuild.start()
>> @@ -70,9 +94,12 @@
>>              if self.currentBuild.process is not None:
>>                  if self.currentBuild.process.poll() is None:
>>                      self.currentBuild.process.terminate()
>> -            # Give it 5 seconds before sending kill.
...

Read more...

Revision history for this message
Guilherme Salgado (salgado) wrote :
Download full text (9.1 KiB)

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/slave/slave.py'
> >> - def startBuild(self, projectName, configBranchURL):
> >> + def add_user_to_bzr_ssh_url(self, configBranchURL, lpUser):
> >> + # check for lp: format URLs. Convert these to bzr+ssh ones
> >> + configBranchURL = re.sub("lp:", "bzr+ssh://bazaar.launchpad.net/",
> >> + configBranchURL)
> >
> > I thought you were going to use 'bzr lp-login' as it's not a problem if
> > we fail to cleanup ~/.bazaar/authentication.conf as long as we make sure
> > 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/authentication.conf to the build script
> 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_user_to_bzr_ssh_url(configBranchURL, lpUser)
> >> +
> >> self.currentBuild = ProjectBuildProcess(
> >> - self, projectName, configBranchURL)
> >> + self, pr...

Read more...

Revision history for this message
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/authentication.conf to the build script
>> 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 ProjectBuildProcess.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.

Revision history for this message
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/authentication.conf to the build script
> >> 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://launchpad.net/~salgado>

Revision history for this message
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

Revision history for this message
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.

Revision history for this message
Guilherme Salgado (salgado) wrote :

Hm, there seems to be a failing test on this branch.

./.virtualenv/bin/nosetests offspring.slave
F...
======================================================================
FAIL: test_checkSSHAgent (test_private_ssh_keys.TestLexbuilderSlavePrivateSSHKeys)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/salgado/devel/offspring/private-builds/lib/offspring/slave/tests/test_private_ssh_keys.py", line 98, in test_checkSSHAgent
    "Identity added: <path to...>/offspring_test_key, found:\n" + lines[1])
AssertionError: Test build ssh-agent check failed: Expected to find Identity added: <path to...>/offspring_test_key, found:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-------------------- >> begin captured logging << --------------------
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
--------------------- >> end captured logging << ---------------------

----------------------------------------------------------------------
Ran 4 tests in 2.049s

FAILED (failures=1)

Revision history for this message
Guilherme Salgado (salgado) wrote :
Download full text (18.3 KiB)

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/slave/slave.py'
> --- lib/offspring/slave/slave.py 2011-09-23 14:01:26 +0000
> +++ lib/offspring/slave/slave.py 2011-10-13 18:55:08 +0000
> @@ -57,9 +60,30 @@
> self.server.stop()
> self.server.join()
>
> - def startBuild(self, projectName, configBranchURL):
> + def add_user_to_bzr_ssh_url(self, configBranchURL, lpUser):

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://bazaar.launchpad.net/",
> + configBranchURL)
> +
> + # The assumption is that this function will only be called with a
> + # bzr+ssh URL (hence the name!)
> + assert(re.search("bzr\+ssh://", configBranchURL))
> +
> + # Add user to URL
> + configBranchURL = re.sub("bzr\+ssh://",
> + "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_user_to_bzr_ssh_url(configBranchURL, lpUser)
> +
> self.currentBuild = ProjectBuildProcess(
> - self, projectName, configBranchURL)
> + self, projectName, configBranchURL, lpSSHKey)
> logging.info("Starting build: %s %s %s" % (
> self.currentBuild.getCommandPath(), projectName, configBranchURL))
> self.currentBuild.start()
> @@ -159,14 +186,34 @@
>
> class ProjectBuildProcess(MonitoredProcess):
>
> - def __init__(self, supervisor, projectName, configBranchURL):
> - MonitoredProcess.__init__(
> - 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.NamedTemporaryFile()
> + args.append(self.ssh_key_file.name)
> + self.ssh_key_file.write(lpSSHKey)
> + self.ssh_key_file.flush()
> +
> + MonitoredProcess.__init__(self, supervisor, args,

Nitpick: the indentation here was ...

review: Needs Fixing

Unmerged revisions

67. By James Tunnicliffe

Made failures in test_checkSSHAgent easier to debug by providing sensible error messages.

66. By James Tunnicliffe

Made ProjectBuildProcess.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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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:

Subscribers

People subscribed via source and target branches