Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adds documentation for running IMDS locally #512

Merged
merged 4 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions apps/confidential/confidential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (

// errorClient is an HTTP client for tests that should fail when confidential.Client sends a request
type errorClient struct{}
type contextKey struct{}

func (*errorClient) Do(req *http.Request) (*http.Response, error) {
return nil, fmt.Errorf("expected no requests but received one for %s", req.URL.String())
Expand Down Expand Up @@ -212,7 +213,7 @@ func TestAcquireTokenOnBehalfOf(t *testing.T) {

func TestAcquireTokenByAssertionCallback(t *testing.T) {
calls := 0
key := struct{}{}
key := contextKey{}
ctx := context.WithValue(context.Background(), key, true)
getAssertion := func(c context.Context, o AssertionRequestOptions) (string, error) {
if v := c.Value(key); v == nil || !v.(bool) {
Expand Down Expand Up @@ -631,7 +632,7 @@ func TestNewCredFromTokenProvider(t *testing.T) {
expectedToken := "expected token"
called := false
expiresIn := 4200
key := struct{}{}
key := contextKey{}
ctx := context.WithValue(context.Background(), key, true)
cred := NewCredFromTokenProvider(func(c context.Context, tp exported.TokenProviderParameters) (exported.TokenProviderResult, error) {
if called {
Expand Down Expand Up @@ -887,7 +888,7 @@ func TestWithClaims(t *testing.T) {
case "obo":
ar, err = client.AcquireTokenOnBehalfOf(ctx, "assertion", tokenScope, WithClaims(test.claims))
default:
t.Fatalf("test bug: no test for " + method)
t.Fatalf("test bug: no test for %s", method)
}
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -997,7 +998,7 @@ func TestWithTenantID(t *testing.T) {
case "obo":
ar, err = client.AcquireTokenOnBehalfOf(ctx, "assertion", tokenScope, WithTenantID(test.tenant))
default:
t.Fatalf("test bug: no test for " + method)
t.Fatalf("test bug: no test for %s", method)
}
if err != nil {
if test.expectError {
Expand Down
2 changes: 1 addition & 1 deletion apps/internal/base/internal/storage/items_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func TestContractUnmarshalJSON(t *testing.T) {
}
if diff := pretty.Compare(want, got); diff != "" {
t.Errorf("TestContractUnmarshalJSON: -want/+got:\n%s", diff)
t.Errorf(string(got.AdditionalFields["unknownEntity"].(stdJSON.RawMessage)))
t.Errorf("%s", string(got.AdditionalFields["unknownEntity"].(stdJSON.RawMessage)))
}
}

Expand Down
3 changes: 2 additions & 1 deletion apps/internal/local/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
// Note: It is a little weird we handle some errors by not going to the failPage. If they all should,
// change this to s.error() and make s.error() write the failPage instead of an error code.
_, _ = w.Write([]byte(fmt.Sprintf(failPage, headerErr, desc)))
s.putResult(Result{Err: fmt.Errorf(desc)})
s.putResult(Result{Err: fmt.Errorf("%s", desc)})

return
}

Expand Down
2 changes: 1 addition & 1 deletion apps/managedidentity/managedidentity.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func SystemAssigned() ID {
type Client struct {
httpClient ops.HTTPClient
miType ID
source Source
// source Source reenable when required in future sources
}

type ClientOptions struct {
Expand Down
6 changes: 3 additions & 3 deletions apps/public/public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ func TestAcquireTokenWithTenantID(t *testing.T) {
case "password":
ar, err = client.AcquireTokenByUsernamePassword(ctx, tokenScope, "username", "password", WithTenantID(test.tenant))
default:
t.Fatalf("test bug: no test for " + method)
t.Fatalf("test bug: no test for %s", method)
}
if err != nil {
if test.expectError {
Expand Down Expand Up @@ -620,7 +620,7 @@ func TestWithClaims(t *testing.T) {
client.base.Token.WSTrust = fake.WSTrust{SamlTokenInfo: wstrust.SamlTokenInfo{AssertionType: "urn:ietf:params:oauth:grant-type:saml1_1-bearer"}}
ar, err = client.AcquireTokenByUsernamePassword(ctx, tokenScope, "username", "password", WithClaims(test.claims))
default:
t.Fatalf("test bug: no test for " + method)
t.Fatalf("test bug: no test for %s", method)
}
if method == "devicecode" && err == nil {
// complete the device code flow
Expand Down Expand Up @@ -910,7 +910,7 @@ func TestWithAuthenticationScheme(t *testing.T) {
case "password":
ar, err = client.AcquireTokenByUsernamePassword(ctx, tokenScope, "username", "password", WithAuthenticationScheme(authScheme))
default:
t.Fatalf("test bug: no test for " + testCase.name)
t.Fatalf("test bug: no test for %s", testCase.name)
}

// validate that the token is created correctly
Expand Down
11 changes: 5 additions & 6 deletions apps/tests/devapps/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import (
)

var (
config = CreateConfig("config.json")
//config = CreateConfig("config.json") reenable when config is implemented
cacheAccessor = &TokenCache{file: "serialized_cache.json"}
)

func main() {
ctx := context.Background()

// Choose a sammple to run.
exampleType := "7"
exampleType := "5"

if exampleType == "1" {
acquireTokenDeviceCode()
Expand All @@ -37,9 +37,8 @@ func main() {
// This sample does not use a serialized cache - it relies on in-memory cache by reusing the app object
// This works well for app tokens, because there is only 1 token per resource, per tenant.
acquireTokenClientCertificate()
// // this time the token comes from the cache!
// acquireTokenClientCertificate()
} else if exampleType == "7" {
RunManagedIdentity()

// this time the token comes from the cache!
acquireTokenClientCertificate()
}
}
138 changes: 138 additions & 0 deletions apps/tests/devapps/managedidentity/docs/msi_manual_testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Running Managed Identity Sources

This file will talk you through the steps required to run the each of the Managed Identity Sources locally

## Setup Virtual Environment and Run Tests for IMDS

To test locally we will require a virtual machine, which we can set up in [Azure](https://portal.azure.com/?feature.tokencaching=true&feature.internalgraphapiversion=true#home)
The next few steps will go over each step of the process, from creating resource groups to managed identity

### Setup Resource Group

We need to set up a resource group that our virtual machine will use

1. On the Azure home screen linked above, you should see **'Resource Groups'**, click this
2. Click **'Create'**
3. Select your Subscription, and below that enter a Resource Group name, such as ***'go-lang-rg'***
4. Select a region, for example **'West Europe'**
5. At the bottom of the screen click **'Review + Create'**
6. At the bottom of the screen click **'Create'**
7. Your resource group is created, you can now go back to the Azure homepage

### Setup Virtual Machine

Next, we need to set up a Virtual Machine for IMDS to run on. Make sure you are on the Azure Homepage

1. Click **'Create a Resource'**
2. Under **'Virtual Machine'** click **'Create'**
3. Select the same Subscription you did for the Resource Group creation
4. Under Resource Group you should see the one you created prior, in this example, ***'go-lang-rg'***. Select it
5. Create a name for your Virtual Machine, such as ***'go-lang-machine'***
6. Select the region, such as West Europe
7. Most of the options can be kept the same

```
- Availability Options = Availability Zone
- Zone Options = Self Selected Zone
- Security Type = Trusted Launch Virtual Machines
- Image = Ubuntu Server 24.04 LTS -x64 Gen2
- VM Architecture = x64
- Run with Azure Spot Discount = Off
- Size = Standard_D2s_v3 - 2 vcpus, 8 GiB memory
- Enable Hibernation = Off
- Authentication Type = SSH Public Key
- SSH Public Key Source = Generate New Key Pair
- SSH Key Type = RSA SSH Format
- Public Inbound Ports = Allow Selected Ports
- Select Inbound Ports = SSH(22)
```

8. For username, enter whatever you want, for example ***'go-lang-machine'***
9. For Key Pair Name, set it to whatever you want, i.e ***'go-lang-machine-key'***
10. At the bottom of the screen click on **'Review + Create'**
11. Click **'Create'** at the bottom of the next page
12. You will see a popup to Generate a New Key Pair
13. Click **'Download private key and create resource'**
14. Once downloaded it should redirect to a page that shows the Virtual Machine deployment. When it completes you can go back to the Home Screen
15. To do the following steps you need to ensure you Virtual Machine is running. On the homepage you can see your virtual machine i.e ***'go-lang-machine'***, click on it
16. In the **'Overview'** of the virtual machine, you can see if it is started or not. If it is not started click on **'Start'**
17. Go back to the homepage

### Setup Local Machine

The next step involves using SSH and setting up the repo on the Virtual Machine
In this example it is done using Mac, if using Windows just make the required adjustments

1. You should have the private key downloaded using the name you set previously, i.e ***'go-lang-machine-key'***. In this example the key is saved in the Downloads folder
2. Open up Terminal
3. Run the following command, using the key name name you setup prior, in this case `chmod 700 go-lang-machine-key.pem`
4. On your Azure home page, click into your Virtual Machine that you created prior, i.e ***'go-lang-machine'***
5. In the left hand panel click expand the **'Connect'** section, and click on **'Connect'**
6. Select the **'Native SSH'** option
7. In the 3rd section copy the command shown i.e `ssh -i ~/Downloads/KEY-NAME.pem VIRTUAL-MACHINE-NAME@PUBLIC-IP-ADDRESS`
8. In Terminal, run the command you copied
9. It will ask you if you are sure you want to continue connecting, type **'yes'**, this will add your public IP to the list of known hosts
10. We should now be connected to the VM via SSH, if not, run the command again
11. Clone our library by calling `git clone https://github.com/AzureAD/microsoft-authentication-library-for-go.git`
12. **cd** into `microsoft-authentication-library-for-go/`
13. Change to whatever branch you want to test on i.e `git switch YOUR_BRANCH_NAME`
14. Perform an update with this command `sudo apt-get update`
15. Install go via `sudo apt-get install golang`, it might ask are you sure you want to install, say yes
16. **cd** into `apps/tests/devapps/managedidentity`
17. Run `go run managedidentity_sample.go`
18. You should see any changes be committed to the SSH instance of the library, and receive some error along the lines of **"Identity not found"**
19. The next steps will talk through running System Assigned and User Assigned

### Run System Assigned Test

1. From the Azure homepage click on your virtual machine
2. In the left hand menu click on Security, and then Identity
3. You should see two tabs, System Assigned and User Assigned. Click on System Assigned if not already accepted
4. Click Status so it is **'On'**
5. Click Save and then Yes
6. Wait for it to finish
7. When done, run the command from step 15 in **'Setup Local Machine'**
8. You should see **'token expire at : some expiry date'**, where **'some expiry date'** is an expiry that is not all 0's, i.e
`2024-09-26 22:05:11.532734044 +0000 UTC m=+86400.490900710`

### Run User Assigned Test

1. From Azure homepage click on **'Create a resource'**
2. Search for **'Managed Identity'**
3. You should see **'User Assigned Managed Identity'**, under it click **'Create'**
4. Under **'Create'** click on **'User Assigned Managed Identity'**
5. Select your subscription
6. Select the resource group you created earlier
7. Select your region, i.e West Europe
8. Put in a name i.e ***'go-lang-mi'***
9. Click **'Review + Create'** at the bottom of the page
10. Click on **'Create'** at the bottom
11. When it is deployed go back to the Azure homepage
12. Click on the virtual machine you created earlier
13. In the left hand panel click on **'Security'**, in the expanded menu click on **'Identity'**
14. At the top select the **'User Assigned'** tab
15. Click on **'Add'**
16. When it has deployed click into the managed identity in the User Assigned tab
17. Copy the client ID
18. In your local instance of **'microsoft-authentication-library-for-go'**, open `managedidentity_sample.go`
19. Change the following:
```
'miSystemAssigned, err := mi.New(mi.SystemAssigned())'
```
to be
```
'miUserAssigned, err := mi.New(mi.UserAssignedClientID(CLIENT_ID_YOU_COPIED))'
```
20. Update anything that was previously `miSystemAssigned`, to be `miUserAssigned`
21. Run the command from step 15 in **'Setup Local Machine'**
22. You should see **'token expire at : some expiry date'**, where **'some expiry date'** is an expiry that is not all 0's, i.e
`2024-09-26 22:05:11.532734044 +0000 UTC m=+86400.490900710`

## Useful command for local testing

This command first synchronizes the local microsoft-authentication-library-for-go directory (including code changes), with the corresponding directory on a remote virtual machine using rsync. After the synchronization, it connects to the remote machine via SSH and runs the go application in the correct directory.
This is useful when the developer is not working on the server machine itself

```
rsync -avz -e "ssh -i PATH_TO_YOUR_PEM_FILE.pem" PATH_TO_THE_GO_LIB/microsoft-authentication-library-for-go/VIRTUAL-MACHINE-NAME@PUBLIC-IP-ADDRESS:/home/VIRTUAL-MACHINE-NAME/PATH_TO_GO_LIB/microsoft-authentication-library-for-go && ssh -i PATH_TO_YOUR_PEM_FILE.pem VIRTUAL-MACHINE-NAME@PUBLIC-IP-ADDRESS 'cd microsoft-authentication-library-for-go/apps/tests/devapps/managedidentity && go run managedidentity_sample.go'
```
42 changes: 42 additions & 0 deletions apps/tests/devapps/managedidentity/managedidentity_sample.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"context"
"fmt"

mi "github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity"
)

func runIMDSSystemAssigned() {
miSystemAssigned, err := mi.New(mi.SystemAssigned())
if err != nil {
fmt.Println(err)
}
result, err := miSystemAssigned.AcquireToken(context.Background(), "https://management.azure.com/")
if err != nil {
fmt.Println(err)
}
fmt.Println("token expire at : ", result.ExpiresOn)
}

func runIMDSUserAssigned() {
miUserAssigned, err := mi.New(mi.UserAssignedClientID("YOUR_MANAGED_IDENTITY_CLIENT_ID"))
if err != nil {
fmt.Println(err)
}
result, err := miUserAssigned.AcquireToken(context.Background(), "https://management.azure.com/")
if err != nil {
fmt.Println(err)
}
fmt.Println("token expire at : ", result.ExpiresOn)
}

func main() {
exampleType := "1"

if exampleType == "1" {
runIMDSSystemAssigned()
} else if exampleType == "2" {
runIMDSUserAssigned()
}
}
20 changes: 0 additions & 20 deletions apps/tests/devapps/managedidentity_sample.go

This file was deleted.