Strange certificate errors: Is there any kind of outbound proxy?

I’m new to fly.io. I just posted my first app, which is really simple golang app that just displays a webpage and allows a user to log in using Google. It works fine on my local machine as well as on a dedicated VM hosted on Google Cloud.

When I tried running it on fly.io, though, I got an error saying the certificate is signed by an unknown authority. The error occurred during the Exchange step.

I modified my code to accept untrusted certificates and now it works fine. I’m wondering if something is happening between my fly.io app and Google? It almost looks like a man-in-the-middle attack. If it’s helpful, here’s the code:

func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
	log.Println("handleGoogleCallback")
	c := googleOauthConfig()
	state := r.FormValue("state")
	if state != oauthStateString {
		log.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	code := r.FormValue("code")

	log.Printf("Code: %s", code)
	log.Printf("Config: %+v", c)

	// Custom HTTP client with TLS config (this is needed to avoid the certificate error)
	
	httpClient := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // Skip TLS verification
		},
	}
	
	// Use custom HTTP client in OAuth2 exchange
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
	token, err := c.Exchange(ctx, code)

	//token, err := c.Exchange(context.Background(), code)
	if err != nil {
		log.Printf("Code exchange failed: %s", err.Error())
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	response, err := httpClient.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken)
	if err != nil {
		log.Printf("Failed getting user info: %s", err.Error())
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	defer response.Body.Close()

	var userInfo struct {
		Email string `json:"email"`
	}
	json.NewDecoder(response.Body).Decode(&userInfo)

	s := new(Session)
	s.Token = *token
	s.Email = userInfo.Email

	err = s.Set(w)
	if err != nil {
		log.Printf("Failed to set session: %s", err.Error())
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}
1 Like

Now I’m having the same issue trying to hit an OpenAI endpoint. It works fine locally and throws an error on fly.io:

error getting response: Post “https://api.openai.com/v1/chat/completions”: tls: failed to verify certificate: x509: certificate signed by unknown authority

The library that I’m using to hit OpenAI is a third-party library so I’m really confused by this one.

Hi, this means that when you try to hit a given server, the certificate received from that server cannot be verified because your host (meaning: your fly.io VM) does not have the authority-issued certificates it needs to verify the signature.

These are typically provided by a ca-certificates package in the Linux distro you’re using for the image you deployed. What you’re seeing is relatively common with very minimal base distros (Alpine?) that don’t include ca-certificates by default. So you can either install the corresponding distro package, or, depending on which language/framework you’re using, install a specific library that contains the certificates; example, for Python you can install the ca-certificates wheel in your virtualenv.

tl;dr try installing your distro’s ca-certificates package and that should get you going.

  • Daniel
2 Likes

Thanks, Daniel! For anyone having this same issue, I added this to my Dockerfile:

# Update package lists and install ca-certificates
RUN apt-get update && apt-get install -y \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

And now it works. Much appreciated. What a strange lib omission for an image that usually runs backend services.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.