Skip to content
New issue

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

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

Already on GitHub? # to your account

Panic when using STS with certificate #1769

Closed
kukulam-form3 opened this issue Feb 9, 2023 · 3 comments · Fixed by #1770
Closed

Panic when using STS with certificate #1769

kukulam-form3 opened this issue Feb 9, 2023 · 3 comments · Fixed by #1770

Comments

@kukulam-form3
Copy link

Description

Unable to use minio-go with STS with certificate.

Calling credentials.NewSTSCertificateIdentity triggers panic

How to repeat problem ?

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"log"
	"net"
	"net/http"
	"os"
	"time"
)

func main() {
	endpoint := "localhost:9000"
	certPath := "public.crt"
	keyPath := "private.key"
	caPath := "ca.crt"

	caCert, err := os.ReadFile(caPath)
	if err != nil {
		log.Printf("unable to setup CA certificate: %v", err)
		os.Exit(1)
	}
	var caCertPool *x509.CertPool
	caCertPool = x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	cert, err := tls.LoadX509KeyPair(certPath, keyPath)
	if err != nil {
		log.Printf("unable to setup client certificate: %v", err)
		os.Exit(1)
	}

	// default transportCreds with added CA cert and client cert
	transportCreds := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		ForceAttemptHTTP2:     true,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 5 * time.Second,
		TLSClientConfig: &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      caCertPool,
		},
	}

	creds, err := credentials.NewSTSCertificateIdentity("https://" + endpoint, cert, credentials.CertificateIdentityWithTransport(transportCreds))
	if err != nil {
		log.Printf("unable to setup client credentials: %v", err)
		os.Exit(1)
	}

	transportClient, err := minio.DefaultTransport(true)
	if err != nil {
		log.Printf("unable to init transport layer for minio client: %v", err)
		os.Exit(1)
	}
	transportClient.TLSClientConfig = &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      caCertPool,
	}

	client, err := minio.New(endpoint, &minio.Options{
		Transport: transportCreds,
		Creds:     creds,
		Secure:    true,
	})
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("setup client correctly")

	err = client.MakeBucket(context.Background(), "examplebucket", minio.MakeBucketOptions{})
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("bucket has been created successfully")
}

Output

❯ ./minioexample
2023/02/09 12:58:50 setup client correctly
panic: assignment to entry in nil map

goroutine 1 [running]:
net/url.Values.Add(...)
        /Users/milosz/.gvm/gos/go1.19/src/net/url/url.go:902
github.com/miloszminio-go/v7/pkg/credentials.(*STSCertificateIdentity).Retrieve(0x140001d9960)
        /Users/miloszgit_repo/minio-sandbox/go/minio2/client/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_tls_identity.go:144 +0x2a8
github.com/minio/minio-go/v7/pkg/credentials.(*Credentials).Get(0x140000a4540)
        /Users/milosz/git_repo/minio-sandbox/go/minio2/client/vendor/github.com/minio/minio-go/v7/pkg/credentials/credentials.go:155 +0x108
github.com/minio/minio-go/v7.(*Client).newRequest(0x140000ba370, {0x10500ff30, 0x14000028090}, {0x104eecb22?, 0x0?}, {0x0, {0x104eefe33, 0xd}, {0x0, 0x0}, ...})
        /Users/milosz/git_repo/minio-sandbox/go/minio2/client/vendor/github.com/minio/minio-go/v7/api.go:756 +0x258
github.com/minio/minio-go/v7.(*Client).executeMethod(0x140000ba370, {0x10500ff30, 0x14000028090}, {0x104eecb22, 0x3}, {0x0, {0x104eefe33, 0xd}, {0x0, 0x0}, ...})
        /Users/milosz/git_repo/minio-sandbox/go/minio2/client/vendor/github.com/minio/minio-go/v7/api.go:610 +0x62c
github.com/minio/minio-go/v7.(*Client).doMakeBucket(0x140000ba370, {0x10500ff30, 0x14000028090}, {0x104eefe33, 0xd}, {0x104eee71d, 0x9}, 0xe0?)
        /Users/milosz/git_repo/minio-sandbox/go/minio2/client/vendor/github.com/minio/minio-go/v7/api-put-bucket.go:90 +0x378
github.com/minio/minio-go/v7.(*Client).makeBucket(0x104ef3a57?, {0x10500ff30, 0x14000028090}, {0x104eefe33, 0xd}, {{0x0?, 0x0?}, 0x0?})
        /Users/milosz/git_repo/minio-sandbox/go/minio2/client/vendor/github.com/minio/minio-go/v7/api-put-bucket.go:36 +0x7c
github.com/minio/minio-go/v7.(*Client).MakeBucket(...)
        /Users/milosz/git_repo/minio-sandbox/go/minio2/client/vendor/github.com/minio/minio-go/v7/api-put-bucket.go:122
main.main()
        /Users/milosz/git_repo/minio-sandbox/go/minio2/client/main.go:81 +0x618

What's the reason ?

sts_tls_identity.go

	queryValues := url.Values{}
	queryValues.Set("Action", "AssumeRoleWithCertificate")
	queryValues.Set("Version", STSVersion)
	endpointURL.RawQuery = queryValues.Encode()

	req, err := http.NewRequest(http.MethodPost, endpointURL.String(), nil)
	if err != nil {
		return Value{}, err
	}
	req.Form.Add("DurationSeconds", strconv.FormatUint(uint64(livetime.Seconds()), 10))

req.Form is nil in this situation, function ParseForm is not used

docs

	// Form contains the parsed form data, including both the URL
	// field's query parameters and the PATCH, POST, or PUT form data.
	// This field is only available after ParseForm is called.
	// The HTTP client ignores Form and uses Body instead.
	Form url.Values

How to fix ?

Add DurationSeconds parameter within queryValues:
sts_tls_identity.go

	queryValues := url.Values{}
	queryValues.Set("Action", "AssumeRoleWithCertificate")
	queryValues.Set("Version", STSVersion)
	queryValues.Set("DurationSeconds", strconv.FormatUint(uint64(livetime.Seconds()), 10))
	endpointURL.RawQuery = queryValues.Encode()

	req, err := http.NewRequest(http.MethodPost, endpointURL.String(), nil)
	if err != nil {
		return Value{}, err
	}
klauspost added a commit to klauspost/minio-go that referenced this issue Feb 9, 2023
Initialize request form before writing.

Fixes minio#1769
@klauspost
Copy link
Contributor

#1770 should fix the issue.

@klauspost klauspost reopened this Feb 9, 2023
@kukulam-form3
Copy link
Author

kukulam-form3 commented Feb 9, 2023

#1770 should fix the issue.

Great, thank you 😃 do you know when it will be released ? it's kind of blocker for us.

harshavardhana pushed a commit that referenced this issue Feb 9, 2023
Initialize request form before writing.

Fixes #1769
@harshavardhana
Copy link
Member

harshavardhana commented Feb 9, 2023

#1770 should fix the issue.

Great, thank you smiley do you know when it will be released ? it's kind of blocker for us.

Soon @kukulam-form3 - use go get github.com/minio/minio-go/v7@master for now.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants