diff --git a/common/oauthTokenManager.go b/common/oauthTokenManager.go index fbb022c8a..4c9aa4a65 100644 --- a/common/oauthTokenManager.go +++ b/common/oauthTokenManager.go @@ -21,7 +21,6 @@ package common import ( - "bufio" "context" "crypto/rsa" "crypto/x509" @@ -29,22 +28,20 @@ import ( "encoding/pem" "errors" "fmt" - "io" "io/ioutil" "net" "net/http" "os" "path" "path/filepath" - "runtime" "strconv" "strings" - "syscall" "time" "golang.org/x/crypto/pkcs12" "github.com/Azure/go-autorest/autorest/adal" + "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" ) // ApplicationID represents 1st party ApplicationID for AzCopy. @@ -75,6 +72,30 @@ type UserOAuthTokenManager struct { stashedInfo *OAuthTokenInfo } +// using this from https://github.com/Azure/go-autorest/blob/b3899c1057425994796c92293e931f334af63b4e/autorest/adal/token.go#L1055-L1067 +// this struct works with the adal sdks used in clients and azure-cli token requests +type token struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + + // AAD returns expires_in as a string, ADFS returns it as an int + ExpiresIn json.Number `json:"expires_in"` + // expires_on can be in two formats, a UTC time stamp or the number of seconds. + ExpiresOn string `json:"expires_on"` + NotBefore json.Number `json:"not_before"` + + Resource string `json:"resource"` + Type string `json:"token_type"` +} + +// Environment variables injected in the pod +const ( + AzureClientIDEnvVar = "AZURE_CLIENT_ID" + AzureTenantIDEnvVar = "AZURE_TENANT_ID" + AzureFederatedTokenFileEnvVar = "AZURE_FEDERATED_TOKEN_FILE" // #nosec + AzureAuthorityHostEnvVar = "AZURE_AUTHORITY_HOST" +) + // NewUserOAuthTokenManagerInstance creates a token manager instance. func NewUserOAuthTokenManagerInstance(credCacheOptions CredCacheOptions) *UserOAuthTokenManager { return &UserOAuthTokenManager{ @@ -794,115 +815,76 @@ func (credInfo *OAuthTokenInfo) GetNewTokenFromMSI(ctx context.Context) (*adal.T if credInfo.Token.Resource != "" && credInfo.Token.Resource != targetResource { targetResource = credInfo.Token.Resource } - - // Try Arc VM - req, resp, errArcVM := credInfo.queryIMDS(ctx, MSIEndpointArcVM, targetResource, IMDSAPIVersionArcVM) - if errArcVM != nil { - // Try Azure VM since there was an error in trying Arc VM - reqAzureVM, respAzureVM, errAzureVM := credInfo.queryIMDS(ctx, MSIEndpointAzureVM, targetResource, IMDSAPIVersionAzureVM) - if errAzureVM != nil { - var serr syscall.Errno - if errors.As(errArcVM, &serr) { - econnrefusedValue := -1 - switch runtime.GOOS { - case "linux": - econnrefusedValue = int(syscall.ECONNREFUSED) - case "windows": - econnrefusedValue = WSAECONNREFUSED - } - - if int(serr) == econnrefusedValue { - // If connection to Arc endpoint was refused - return nil, fmt.Errorf("please check whether MSI is enabled on this PC, to enable MSI please refer to https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm#enable-system-assigned-identity-on-an-existing-vm: %v", errAzureVM) - } - - // A syscall error other than ECONNREFUSED, implies we could not get the HTTP response - return nil, fmt.Errorf("error communicating with Arc IMDS endpoint (%s): %v", MSIEndpointArcVM, errArcVM) - } - - // queryIMDS failed, but not with a syscall error - // 1. Either it is an HTTP error, or - // 2. The HTTP request timed out - return nil, fmt.Errorf("invalid response received from Arc IMDS endpoint (%s), probably some unknown process listening: %v", MSIEndpointArcVM, errArcVM) - } - - // Arc IMDS failed with error, but Azure IMDS succeeded - req, resp = reqAzureVM, respAzureVM - } else if !isValidArcResponse(resp) { - // Not valid response from ARC IMDS endpoint. Perhaps some other process listening on it. Try Azure IMDS endpoint as fallback option. - reqAzureVM, respAzureVM, errAzureVM := credInfo.queryIMDS(ctx, MSIEndpointAzureVM, targetResource, IMDSAPIVersionAzureVM) - if errAzureVM != nil { - // Neither Arc nor Azure VM IMDS endpoint available. Can't use MSI. - return nil, fmt.Errorf("invalid response received from Arc IMDS endpoint (%s), probably some unknown process listening. If this an Azure VM, please check whether MSI is enabled, to enable MSI please refer to https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm#enable-system-assigned-identity-on-an-existing-vm: %v", MSIEndpointArcVM, errAzureVM) - } - - // Azure VM IMDS endpoint ok! - req, resp = reqAzureVM, respAzureVM - } else { - // Valid response received from ARC IMDS endpoint. Proceed with the next step. - challengeTokenPath := strings.Split(resp.Header["Www-Authenticate"][0], "=")[1] - // Open the file. - challengeTokenFile, fileErr := os.Open(challengeTokenPath) - if os.IsPermission(fileErr) { - switch runtime.GOOS { - case "linux": - return nil, fmt.Errorf("permission level inadequate to read Arc challenge token file %s. Make sure you are running AzCopy as a user who is a member of the \"himds\" group or is superuser.", challengeTokenPath) - case "windows": - return nil, fmt.Errorf("permission level inadequate to read Arc challenge token file %s. Make sure you are running AzCopy as a user who is a member of the \"local Administrators\" group or the \"Hybrid Agent Extension Applications\" group.", challengeTokenPath) - default: - return nil, fmt.Errorf("error occurred while opening file %s in unsupported GOOS %s: %v", challengeTokenPath, runtime.GOOS, fileErr) - } - } else if fileErr != nil { - return nil, fmt.Errorf("error occurred while opening file %s: %v", challengeTokenPath, fileErr) - } - - defer challengeTokenFile.Close() - - // Create a new Reader for the file. - reader := bufio.NewReader(challengeTokenFile) - challengeToken, fileErr := reader.ReadString('\n') - if fileErr != nil && fileErr != io.EOF { - return nil, fmt.Errorf("error occurred while reading file %s: %v", challengeTokenPath, fileErr) - } - - req.Header.Set("Authorization", "Basic "+challengeToken) - - resp, errArcVM = msiTokenHTTPClient.Do(req) - if errArcVM != nil { - return nil, fmt.Errorf("failed to query token from Arc IMDS endpoint: %v", errArcVM) - } + // doTokenRequest was copied from https://github.com/Azure/azure-workload-identity/blob/main/pkg/proxy/proxy.go + // this makes it possible to use azcopy with Workload Identity, without requiring a proxy container + token, err := doTokenRequest(ctx, + os.Getenv(AzureClientIDEnvVar), + targetResource, + os.Getenv(AzureTenantIDEnvVar), + os.Getenv(AzureAuthorityHostEnvVar), + ) + if err != nil { + return nil, fmt.Errorf("failed to get token: %s", err) + } + return &adal.Token{ + AccessToken: token.AccessToken, + Resource: token.Resource, + Type: token.Type, + ExpiresOn: json.Number(token.ExpiresOn), + ExpiresIn: token.ExpiresIn, + NotBefore: token.NotBefore, + RefreshToken: token.RefreshToken, + }, nil +} + +func doTokenRequest(ctx context.Context, clientID, resource, tenantID, authorityHost string) (*token, error) { + tokenFilePath := os.Getenv(AzureFederatedTokenFileEnvVar) + cred := confidential.NewCredFromAssertionCallback( + func( + context.Context, + confidential.AssertionRequestOptions, + ) (string, error) { + return readJWTFromFS(tokenFilePath) + }) + + confidentialClientApp, err := confidential.New(clientID, cred, + confidential.WithAuthority(fmt.Sprintf("%s%s/oauth2/token", authorityHost, tenantID))) + if err != nil { + return nil, fmt.Errorf("failed to create confidential client app: %s", err) } - defer func() { // resp and Body should not be nil - io.Copy(ioutil.Discard, resp.Body) - resp.Body.Close() - }() - - // Check if the status code indicates success - // The request returns 200 currently, add 201 and 202 as well for possible extension. - if !(HTTPResponseExtension{Response: resp}).IsSuccessStatusCode(http.StatusOK, http.StatusCreated, http.StatusAccepted) { - return nil, fmt.Errorf("failed to get token from msi, status code: %v", resp.StatusCode) + // ref: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/747 + // For MSAL (v2.0 endpoint) asking an access token for a resource that accepts a v1.0 access token, + // Azure AD parses the desired audience from the requested scope by taking everything before the + // last slash and using it as the resource identifier. + // For example, if the scope is "https://vault.azure.net/.default", the resource identifier is "https://vault.azure.net". + // If the scope is "http://database.windows.net//.default", the resource identifier is "http://database.windows.net/". + scope := resource + if !strings.HasPrefix(scope, "/.default") { + scope = scope + "/.default" } - - b, err := ioutil.ReadAll(resp.Body) + result, err := confidentialClientApp.AcquireTokenByCredential(ctx, []string{scope}) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to acquire token: %s", err) } - result := &adal.Token{} - if len(b) > 0 { - b = ByteSliceExtension{ByteSlice: b}.RemoveBOM() - // Unmarshal will give an error for Go version >= 1.14 for a field with blank values. Arc-server endpoint API returns blank for "not_before" field. - // TODO: Remove fixup once Arc team fixes the issue. - b = fixupTokenJson(b) - if err := json.Unmarshal(b, result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response body: %v", err) - } - } else { - return nil, errors.New("failed to get token from msi") - } + return &token{ + AccessToken: result.AccessToken, + Resource: resource, + Type: "Bearer", + // There is a difference in parsing between the azure sdks and how azure-cli works + // Using the unix time to be consistent with response from IMDS which works with + // all the clients. + ExpiresOn: strconv.FormatInt(result.ExpiresOn.UTC().Unix(), 10), + }, nil +} - return result, nil +func readJWTFromFS(tokenFilePath string) (string, error) { + token, err := os.ReadFile(tokenFilePath) + if err != nil { + return "", err + } + return string(token), nil } // RefreshTokenWithUserCredential gets new token with user credential through refresh. diff --git a/go.mod b/go.mod index e0e7067d5..918df836b 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Azure/azure-storage-blob-go v0.15.0 github.com/Azure/azure-storage-file-go v0.6.1-0.20201111053559-3c1754dc00a5 github.com/Azure/go-autorest/autorest/adal v0.9.18 + github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 github.com/JeffreyRichter/enum v0.0.0-20180725232043-2567042f9cda github.com/danieljoos/wincred v1.1.2 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da @@ -36,13 +37,14 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-ini/ini v1.66.4 // indirect - github.com/golang-jwt/jwt/v4 v4.3.0 // indirect + github.com/golang-jwt/jwt/v4 v4.4.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.7 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 6ad20a705..15b6430e3 100644 --- a/go.sum +++ b/go.sum @@ -57,18 +57,9 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.21.0 h1:HwnT2u2D309SFDHQII6m18HlrCi3jAXhUMTLOWXYH14= cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-pipeline-go v0.2.4-0.20220420205509-9c760f3e9499 h1:eVXzrNOutCSxn7gYn2Tb2alO/D41vX6EyDoRhByS4zc= -github.com/Azure/azure-pipeline-go v0.2.4-0.20220420205509-9c760f3e9499/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-pipeline-go v0.2.4-0.20220425205405-09e6f201e1e4 h1:hDJImUzpTAeIw/UasFUUDB/+UsZm5Q/6x2/jKKvEUiw= github.com/Azure/azure-pipeline-go v0.2.4-0.20220425205405-09e6f201e1e4/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-storage-blob-go v0.13.1-0.20220307213743-78b465951faf h1:81jHLpY81IPdZqBzsnudRpQM1E9xk+ZzBhhJm7BEvcY= -github.com/Azure/azure-storage-blob-go v0.13.1-0.20220307213743-78b465951faf/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= -github.com/Azure/azure-storage-blob-go v0.13.1-0.20220418210520-914dace75d43 h1:/yh9OPVjemL4n8CaXc+GpFTvSlotRFj2HXJIgLo2gG8= -github.com/Azure/azure-storage-blob-go v0.13.1-0.20220418210520-914dace75d43/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58= -github.com/Azure/azure-storage-blob-go v0.13.1-0.20220418220008-28ac0a48144e h1:uGef/l7KHdWy6XTwhnEB4IhJEisPLe0TDfLVthiVL04= -github.com/Azure/azure-storage-blob-go v0.13.1-0.20220418220008-28ac0a48144e/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58= github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk= github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58= github.com/Azure/azure-storage-file-go v0.6.1-0.20201111053559-3c1754dc00a5 h1:aHEvBM4oXIWSTOVdL55nCYXO0Cl7ie3Ui5xMQhLVez8= @@ -86,6 +77,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 h1:VgSJlZH5u0k2qxSpqyghcFQKmvYckj46uymKK5XzkBM= +github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/JeffreyRichter/enum v0.0.0-20180725232043-2567042f9cda h1:NOo6+gM9NNPJ3W56nxOKb4164LEw094U0C8zYQM8mQU= @@ -134,8 +127,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-ini/ini v1.66.4 h1:dKjMqkcbkzfddhIhyglTPgMoJnkvmG+bSLrU9cTHc5M= github.com/go-ini/ini v1.66.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -207,6 +200,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -236,6 +230,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-ieproxy v0.0.3 h1:YkaHmK1CzE5C4O7A3hv3TCbfNDPSCf0RKZFX+VhBeYk= github.com/mattn/go-ieproxy v0.0.3/go.mod h1:6ZpRmhBaYuBX1U2za+9rC9iCGLsSp2tftelZne7CPko= @@ -243,7 +239,9 @@ github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJ github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -441,6 +439,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=