zitadel / zitadel-go

ZITADEL Go - The official client library of ZITADEL for an easy integration into your Go project.
https://zitadel.com
Apache License 2.0
73 stars 29 forks source link

tls: failed to verify certificate: x509: certificate signed by unknown authority #405

Open berupp opened 6 days ago

berupp commented 6 days ago

I am trying to circumvent the server certificate check:

conf := z.New(url.Hostname())
c, err := client.New(ctx, conf,
    client.WithAuth(client.DefaultServiceUserAuthentication(machineKeyFile, oidc.ScopeOpenID, client.ScopeZitadelAPI())),
        client.WithGRPCDialOptions(grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}))),
)

Unfortunately, it seems zitadel-go is ignoring the provided tls.Config and performs the server certificate validation anyways.

Expectation

zitadel-go offering grpc.DialOptions to customize the underlying grpc connection should honour the provided configuration options.

fforootd commented 5 days ago

Hm since it does not work may I recommend a workaround?

To my understanding Go uses the systems CA store, so adding the signing cert there from the internal CA should allow to make it work.

berupp commented 5 days ago

I figured it out, but there is some severe lack of documentation paired with implicit logic that makes this quite annoying to figure out.

Firstly, it seems that zitadel-go uses the http.DefaultClient when setting up the connection initially. Overwriting the tls.Config on http.DefaultTransport allowed me to successfully set up the client and get past the error.

Fortunately zitadel-go, allows you to provide your own http.Client so you don't have to modify the DefaultTransport. With my own client and tls.Config with InsecureSkipVerify:true, I am now successfully connected.

But then, when sending an actual API request, it fails again with the same error. Now HERE is where the

client.WithGRPCDialOptions(grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}))),

is actually required and used.

So the following allows me to actually skip verification of the certificate chain and interact with Zitadel.

conf := z.New(URL.Hostname())
    c, err := client.New(ctx, conf,
        client.WithAuth(func(ctx context.Context, issuer string) (oauth2.TokenSource, error) {
            return profile.NewJWTProfileTokenSourceFromKeyFile(ctx, zitadelURL, machineKeyFile, []string{oidc.ScopeOpenID, client.ScopeZitadelAPI()},
                profile.WithHTTPClient(&http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}))
        }),
        client.WithGRPCDialOptions(grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}))),
    )
    if err != nil {
        return nil, fmt.Errorf("unable to initialize Zitadel client: %w", err)
    }

It is quite unfortunate that this is documented so poorly and essentially requires you to debug through the code in order to understand what is happening.