microsoft / codespace-features

Devcontainer features for use in Codespaces
MIT License
12 stars 8 forks source link

Add support for OIDC during prebuild #63

Closed markphip closed 4 months ago

markphip commented 4 months ago

Secret-less Azure DevOps Prebuilds

It is possible to avoid using PATs entirely and dynamically obtain a token during prebuild using OIDC. This requires creating a Managed Identity or App Registration in Entra, and creating a Federated Identity Credential on the Service Principal for the branch you are prebuilding. The Service Principal created must also be added to Azure DevOps and given permission to the repositories and feeds you will be accessing during the prebuild process. The configuration replaces the cloneSecret with parameters for the Azure clientID and tenantID and also requires adding the feature for the azure-cli:

{
"image": "mcr.microsoft.com/devcontainers/universal:ubuntu",
"features": {
    "ghcr.io/devcontainers/features/azure-cli:1": {},
    "ghcr.io/microsoft/codespace-features/external-repository:latest": {
        "cloneUrl": "https://dev.azure.com/contoso/_git/reposname",
        "clientID": "xxxx-yyyy-zzzz",
        "tenantID": "1111-2222-3333",
        "folder": "/workspaces/ado-repos"
    }
},
"workspaceFolder": "/workspaces/ado-repos",
"initializeCommand": "mkdir -p ${localWorkspaceFolder}/../ado-repos",
"onCreateCommand": "external-git clone",
"postStartCommand": "external-git config"     
}

In this scenario, during the prebuild process an ADO token will be obtained via OIDC and the Federated Identity Credential. This token will be used during the git clone process only. If you have other scripts you are running during onCreateCommand you can run the command external-git prebuild and the ADO token will be sent to stdout for you to use in your scripts to install dependencies from feeds or anything else you may need. The token will only be available during the prebuild process and this has to be done after the clone command so that the OIDC login has already happened.

[!NOTE] You MUST install the Azure CLI feature in your devcontainer.json if using this option

markphip commented 4 months ago

I need to do some manual testing soon, but the expectation I have is that the code will create an environment variable with the name provided in cloneSecret and this will be populated by the clone command and should still be around for anything that comes after that needs to use it. Such as installing npm packages.

Thanks for the fixes. I let Copilot Workspace write some of the code and still need to look at it more closely obviously

markphip commented 4 months ago

@dmichon-msft I did some research and I do not believe the environment variable will still be set when the clone command ends. I think it would be good if there were a way for it to be usable in the rest of the onCreateCommand processing. Open to ideas

dmichon-msft commented 4 months ago

@dmichon-msft I did some research and I do not believe the environment variable will still be set when the clone command ends. I think it would be good if there were a way for it to be usable in the rest of the onCreateCommand processing. Open to ideas

A couple of ideas:

  1. Make a separate CLI command that gets the access token and writes it to stdout (i.e. just abstract away the OIDC interaction from the caller). This command can then just be used to populate an environment variable and pass it into the existing external-git clone command.
  2. Since Azure CLI persists login state if you don't explicitly clear it, currently the caller could get a new token with just another call to az account get-access-token to use for package feed.
markphip commented 4 months ago

So the second option sounds pretty doable. I think we can start with that and as a couple of teams use this we can see if there is something to make it easier.

markphip commented 4 months ago

@dmichon-msft I think I have it now. I was adding the token code in the wrong place anyway. So it is now in a command external-git prebuild that you can run in your scripts and it will echo the token to stdout. This command was already being used as the git credential helper during clone so I moved everything into that and cleaned things up.

markphip commented 4 months ago

I was able to test and verify this all works now. Just doing some final testing of a few error scenarios to make sure they can be diagnosed then will merge and publish