Ver Fonte

Added basic support for service principal login, run ACI e2e tests with it

Guillaume Tardif há 5 anos atrás
pai
commit
eda438aaed
6 ficheiros alterados com 67 adições e 1 exclusões
  1. 7 0
      .github/workflows/ci.yml
  2. 41 0
      azure/login/login.go
  3. 2 1
      cli/mobycli/exec_test.go
  4. 1 0
      go.mod
  5. 2 0
      go.sum
  6. 14 0
      tests/aci-e2e/e2e-aci_test.go

+ 7 - 0
.github/workflows/ci.yml

@@ -55,3 +55,10 @@ jobs:
 
       - name: E2E Test
         run: make e2e-local
+
+      - name: ACI e2e Test
+        env:
+          AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
+          AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
+          AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
+        run: make e2e-aci

+ 41 - 0
azure/login/login.go

@@ -31,6 +31,7 @@ import (
 
 	"github.com/Azure/go-autorest/autorest"
 	"github.com/Azure/go-autorest/autorest/adal"
+	auth2 "github.com/Azure/go-autorest/autorest/azure/auth"
 	"github.com/Azure/go-autorest/autorest/azure/cli"
 	"github.com/Azure/go-autorest/autorest/date"
 	"github.com/pkg/errors"
@@ -93,6 +94,31 @@ func newAzureLoginServiceFromPath(tokenStorePath string, helper apiHelper) (Azur
 	}, nil
 }
 
+// LoginFromServicePrincipal login with clientId / clientSecret from a previously created service principal
+func (login AzureLoginService) LoginFromServicePrincipal(clientID string, clientSecret string, tenantID string) error {
+	// Tried with auth2.NewUsernamePasswordConfig() but could not make this work with username / password, setting this for CI with clientID / clientSecret
+	creds := auth2.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
+
+	spToken, err := creds.ServicePrincipalToken()
+	if err != nil {
+		return errors.Wrapf(errdefs.ErrLoginFailed, "could not  login with service principal: %s", err)
+	}
+	err = spToken.Refresh()
+	if err != nil {
+		return errors.Wrapf(errdefs.ErrLoginFailed, "could not  login with service principal: %s", err)
+	}
+	token, err := spToOAuthToken(spToken.Token())
+	if err != nil {
+		return errors.Wrapf(errdefs.ErrLoginFailed, "could not read service principal token expiry: %s", err)
+	}
+	loginInfo := TokenInfo{TenantID: tenantID, Token: token}
+
+	if err := login.tokenStore.writeLoginInfo(loginInfo); err != nil {
+		return errors.Wrapf(errdefs.ErrLoginFailed, "could not store login info: %s", err)
+	}
+	return nil
+}
+
 // Login performs an Azure login through a web browser
 func (login AzureLoginService) Login(ctx context.Context) error {
 	queryCh := make(chan localResponse, 1)
@@ -179,6 +205,21 @@ func toOAuthToken(token azureToken) oauth2.Token {
 	return oauthToken
 }
 
+func spToOAuthToken(token adal.Token) (oauth2.Token, error) {
+	expiresIn, err := token.ExpiresIn.Int64()
+	if err != nil {
+		return oauth2.Token{}, err
+	}
+	expireTime := time.Now().Add(time.Duration(expiresIn) * time.Second)
+	oauthToken := oauth2.Token{
+		RefreshToken: token.RefreshToken,
+		AccessToken:  token.AccessToken,
+		Expiry:       expireTime,
+		TokenType:    token.Type,
+	}
+	return oauthToken, nil
+}
+
 // NewAuthorizerFromLogin creates an authorizer based on login access token
 func NewAuthorizerFromLogin() (autorest.Authorizer, error) {
 	return newAuthorizerFromLoginStorePath(getTokenStorePath())

+ 2 - 1
cli/mobycli/exec_test.go

@@ -3,9 +3,10 @@ package mobycli
 import (
 	"testing"
 
-	"github.com/docker/api/tests/framework"
 	. "github.com/onsi/gomega"
 	"github.com/stretchr/testify/suite"
+
+	"github.com/docker/api/tests/framework"
 )
 
 type MobyExecSuite struct {

+ 1 - 0
go.mod

@@ -8,6 +8,7 @@ require (
 	github.com/Azure/azure-storage-file-go v0.7.0
 	github.com/Azure/go-autorest/autorest v0.11.0
 	github.com/Azure/go-autorest/autorest/adal v0.9.0
+	github.com/Azure/go-autorest/autorest/azure/auth v0.5.0
 	github.com/Azure/go-autorest/autorest/azure/cli v0.4.0
 	github.com/Azure/go-autorest/autorest/date v0.3.0
 	github.com/Azure/go-autorest/autorest/to v0.4.0

+ 2 - 0
go.sum

@@ -18,6 +18,8 @@ github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKn
 github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
 github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fgsu+GxgDRFyg7f5lM=
 github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.0 h1:nSMjYIe24eBYasAIxt859TxyXef/IqoH+8/g4+LmcVs=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.0/go.mod h1:QRTvSZQpxqm8mSErhnbI+tANIBAKP7B+UIE2z4ypUO0=
 github.com/Azure/go-autorest/autorest/azure/cli v0.4.0 h1:Ml+UCrnlKD+cJmSzrZ/RDcDw86NjkRUpnFh7V5JUhzU=
 github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s=
 github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=

+ 14 - 0
tests/aci-e2e/e2e-aci_test.go

@@ -20,10 +20,12 @@ import (
 	"context"
 	"fmt"
 	"net/url"
+	"os"
 	"strings"
 	"testing"
 
 	"github.com/docker/api/azure"
+	"github.com/docker/api/azure/login"
 
 	"github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/resources/mgmt/resources"
 	azure_storage "github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/storage/mgmt/storage"
@@ -69,6 +71,18 @@ func (s *E2eACISuite) TestContextDefault() {
 }
 
 func (s *E2eACISuite) TestACIBackend() {
+
+	It("Logs in azure using service principal credentials", func() {
+		login, err := login.NewAzureLoginService()
+		Expect(err).To(BeNil())
+		// in order to create new service principal and get these 3 values : `az ad sp create-for-rbac --name 'TestServicePrincipal' --sdk-auth`
+		clientID := os.Getenv("AZURE_CLIENT_ID")
+		clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
+		tenantID := os.Getenv("AZURE_TENANT_ID")
+		err = login.LoginFromServicePrincipal(clientID, clientSecret, tenantID)
+		Expect(err).To(BeNil())
+	})
+
 	It("creates a new aci context for tests", func() {
 		setupTestResourceGroup(resourceGroupName)
 		helper := azure.NewACIResourceGroupHelper()