Getting `Request had invalid authentication credentials` when using service account json key file in Go app - authentication

I'm developing a Go app on a GCP project and I'm using google cloud logging service. I'm having problems in running the app since it's saying that I have invalid authentication credentials when I'm using a service account json key.
Here's the code snippet having the error:
c, cErr := Load(".env")
if cErr != nil {
log.Fatalf("could not load config: %s", cErr)
return
}
// initializes logger which writes to stdout
ctx := context.Background()
opt := option.WithCredentialsFile(c.GoogleApplicationCredentials);
loggerClient, clientErr := logging.NewClient(ctx, "poc-projects-01", opt)
if clientErr != nil {
log.Fatal(clientErr)
}
And here's the definition of the Load() function:
func Load(file string) (*Config, error) {
viper.SetConfigFile(file)
viper.AddConfigPath(".")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
c := &Config{
GoogleApplicationCredentials: viper.GetString("GOOGLE_APPLICATION_CREDENTIALS"),
}
return c, nil
}
I have a .env file with the following content:
GOOGLE_APPLICATION_CREDENTIALS=json/path-to-json.json
I don't know why it's saying token expired even though this is the only service account json key I have on GCP, and on my local machine.

Can you run gcloud auth application-default login and make sure you have that set to the right project.
Check whether GOOGLEAPPLICATIONSCREDENTALS is set with valid JSON key and the environment variable are correctly set, for checking run the below command
echo $GOOGLE_APPLICATION_CREDENTIALS
If the command does not return the correct path to the JSON key, you can set the environment variable with this command:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/json/key.json
Once you have verified that the JSON key is valid and the environment variable is correctly set, you should be able to run your application.Alternatively, you can try to delete the .env file and then recreate it with the service account json key, which should re-generate the token and make it valid.
Attaching troubleshooting documentation for reference.

Related

Uploading a file in selenium/golang

I'm trying to automate a test where I need to upload a file, but I keep getting that the file can't be found.
I found in Selenium page that, for other languages, we have to use a Local File Detector so that the file that's in my computer are sent to the remote server.
The Local File Detector allows the transfer of files from the client machine to the remote server.
But I can't find any function related to that in Go.
I've tried the SendKeys function:
element, err := crediya.el.FindElement(selenium.ByID, "file")
if err != nil {
return fmt.Errorf(errors.Selenium.ElementNotFound+"\n%w", err)
}
if err = element.SendKeys(path); err != nil {
return fmt.Errorf(errors.Selenium.SendKeysFailure+"\n%w", err)
}
but I keep getting:
"unknown error - 61: invalid argument: File not found : "
Note: the element is an input of file type.
Thank you, in advance, for any help provided.
Can someone, please, help me?

Keycloak adaptor for golang application

I am going to secure my golang application using keycloak, but keycloak itself does not support go language.
There are some go adaptor as an open project in github that has implemented openId connect protocol as a provider service, but they do not provide an example or documentation on how to integrate libraries with an application.
How can i interact with keycloak using golang?
As you have pointed out, there is no official keycloak adapter for golang.
But it is pretty straight forward to implement it. Here is a little walk through.
Keycloak server
For this example, I will use the official keycloak docker image to start the server.
The version used is 4.1.0.Final. I think this will work with older KeyCloak versions too though.
docker run -d -p 8080:8080 -e KEYCLOAK_USER=keycloak -e KEYCLOAK_PASSWORD=k --name keycloak jboss/keycloak:4.1.0.Final
After the server is up and running, you can open localhost:8080/auth in your browser, navigate to the administration console and login with username keycloak and k as the corresponding password.
I will not go through the complete process of creating a realm/clients/users. You can look this up under
https://www.keycloak.org/docs/latest/server_admin/index.html#admin-console
Here is just an outline for what I did to reproduce this example:
create a realm named demo
turn off the requirement of ssl for this realm (realmsettings -> login -> require ssl)
create a client named demo-client (change the "Access Type" to confidential)
create a user named demo with password demo (users -> add user). Make sure to activate and impersonate this user.
configure the demo-client to be confidential and use http://localhost:8181/demo/callback as a valid redirect URI.
The resulting keycloak.json (obtained from the installation tab) looks like this:
{
"realm": "demo",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "none",
"resource": "demo-client",
"credentials": {
"secret": "cbfd6e04-a51c-4982-a25b-7aaba4f30c81"
},
"confidential-port": 0
}
Beware that your secret will be different though.
The Go Server
Let's go over to the go server. I use the github.com/coreos/go-oidc package for the heavy lifting:
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"strings"
oidc "github.com/coreos/go-oidc"
"golang.org/x/oauth2"
)
func main() {
configURL := "http://localhost:8080/auth/realms/demo"
ctx := context.Background()
provider, err := oidc.NewProvider(ctx, configURL)
if err != nil {
panic(err)
}
clientID := "demo-client"
clientSecret := "cbfd6e04-a51c-4982-a25b-7aaba4f30c81"
redirectURL := "http://localhost:8181/demo/callback"
// Configure an OpenID Connect aware OAuth2 client.
oauth2Config := oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: redirectURL,
// Discovery returns the OAuth2 endpoints.
Endpoint: provider.Endpoint(),
// "openid" is a required scope for OpenID Connect flows.
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
state := "somestate"
oidcConfig := &oidc.Config{
ClientID: clientID,
}
verifier := provider.Verifier(oidcConfig)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
rawAccessToken := r.Header.Get("Authorization")
if rawAccessToken == "" {
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
return
}
parts := strings.Split(rawAccessToken, " ")
if len(parts) != 2 {
w.WriteHeader(400)
return
}
_, err := verifier.Verify(ctx, parts[1])
if err != nil {
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
return
}
w.Write([]byte("hello world"))
})
http.HandleFunc("/demo/callback", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("state") != state {
http.Error(w, "state did not match", http.StatusBadRequest)
return
}
oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
return
}
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
return
}
resp := struct {
OAuth2Token *oauth2.Token
IDTokenClaims *json.RawMessage // ID Token payload is just JSON.
}{oauth2Token, new(json.RawMessage)}
if err := idToken.Claims(&resp.IDTokenClaims); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data, err := json.MarshalIndent(resp, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(data)
})
log.Fatal(http.ListenAndServe("localhost:8181", nil))
}
This program starts a regular http server with two endpoints. The first one ("/") is your regular endpoint that handles
application logic. In this case, it only returns "hello world" to your client.
The second endpoint ("/demo/callback") is used as a callback for keycloak. This endpoint needs to be registered on your
keycloak server. Keycloak will issue a redirect to this callback URL upon successful user authentication. The redirect contains some additional query parameters. These parameters contain a code that can be used to obtain access/id tokens.
Verify your setup
In order to test this setup you can open a webbrowser and navitage to http://localhost:8181.
The request should reach your go server, which tries to authenticate you. Since you did not send a token, the go server
will redirecty you to keycloak to authenticate.
You should see the login screen of keycloak. Login with the demo user you have created for this realm (demo/demo).
If you have configured your keycloak correctly, it will authenticate you and redirect you to your go server callback.
The end result should be a json like this
{
"OAuth2Token": {
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJsc1hHR2VxSmx3UUZweWVYR0x6b2plZXBYSEhXUngtTHVJTVVLdDBmNmlnIn0.eyJqdGkiOiI5ZjAxNjM2OC1lYmEwLTRiZjMtYTU5Ni1kOGU1MzdmNTNlZGYiLCJleHAiOjE1MzIxNzM2NTIsIm5iZiI6MCwiaWF0IjoxNTMyMTczMzUyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6ImRlbW8tY2xpZW50Iiwic3ViIjoiMzgzMzhjOGItYWQ3Zi00NjlmLTgzOTgtMTc5ODk1ODFiYTEyIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZGVtby1jbGllbnQiLCJhdXRoX3RpbWUiOjE1MzIxNzMzNTIsInNlc3Npb25fc3RhdGUiOiJjZTg2NWFkZC02N2I4LTQ5MDUtOGYwMy05YzE2MDNjMWJhMGQiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6ImRlbW8iLCJlbWFpbCI6ImRlbW9AZGVtby5jb20ifQ.KERz8rBddxM9Qho3kgigX-fClWqbKY-3JcWT3JOQDoLa-prkorfa40BWlyf9ULVgjzT2d8FLJpqQIQYvucKU7Q7vFBVIjTGucUZaE7b6JGMea5H34A1i-MNm7L2CzDJ2GnBONhNwLKoftTSl0prbzwkzcVrps-JAZ6L2gssSa5hBBGJYBKAUfm1OIb57Jq0vzro3vLghZ4Ay7iNunwfcHUrxiFJfUjaU6PQwzrA5pnItOPuavJFUgso7-3JLtn3X9GQuyyZKrkDo6-gzU0JZmkQQzAXXgt43NxooryImuacwSB5xbIKY6qFkedldoOPehld1-oLv0Yy_FIwEad3uLw",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJsc1hHR2VxSmx3UUZweWVYR0x6b2plZXBYSEhXUngtTHVJTVVLdDBmNmlnIn0.eyJqdGkiOiI0MjdmMTlhYy1jMTkzLTQ2YmQtYWFhNi0wY2Q1OTI5NmEwMGQiLCJleHAiOjE1MzIxNzUxNTIsIm5iZiI6MCwiaWF0IjoxNTMyMTczMzUyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6ImRlbW8tY2xpZW50Iiwic3ViIjoiMzgzMzhjOGItYWQ3Zi00NjlmLTgzOTgtMTc5ODk1ODFiYTEyIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6ImRlbW8tY2xpZW50IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiY2U4NjVhZGQtNjdiOC00OTA1LThmMDMtOWMxNjAzYzFiYTBkIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIn0.FvvDW6ZSH8mlRR2zgaN1zesX14SmkCs9RrIVU4Jn1-SHVdKEA6YKur0-RUAFTObQDMLVhFFJ05AjGVGWpBrgVDcAwW2pI9saM-OHlyTJ3VfFoylgfzakVOIpbIDnHO12UaJrkOI9NWPAJdbBOzBHfsDhKbxhjg4ZX8SwlKr42rV4WWuSRcNu4_YDVO19SiXSCKXVldZ1_2S-qPvViq7VZfaoRLHuYyDvma_ByMsmib9JUkevJ8dxsYxVQ5FWaAfFanh1a1f8HxNRI-Cl180oPn1_Tqq_SYwxzBCw7Q_ENkMirwRS1a4cX9yMVEDW2uvKz2D-OiNAUK8d_ONuPEkTGQ",
"expiry": "2018-07-21T13:47:28.986686385+02:00"
},
"IDTokenClaims": {
"jti": "f4d56526-37d9-4d32-b99d-81090e92d3a7",
"exp": 1532173652,
"nbf": 0,
"iat": 1532173352,
"iss": "http://localhost:8080/auth/realms/demo",
"aud": "demo-client",
"sub": "38338c8b-ad7f-469f-8398-17989581ba12",
"typ": "ID",
"azp": "demo-client",
"auth_time": 1532173352,
"session_state": "ce865add-67b8-4905-8f03-9c1603c1ba0d",
"acr": "1",
"email_verified": true,
"preferred_username": "demo",
"email": "demo#demo.com"
}
}
You can copy your access token and use curl to verify if the server is able to accept your tokens:
# use your complete access token here
export TOKEN="eyJhbG..."
curl -H "Authorization: Bearer $TOKEN" localhost:8181
# output hello world
You can try it again after the token has expired - or temper with the token. In case you do it, you should get a redirect to
your keycloak server again.
There is also the gocloak library which provides lot's of functionality. The lib is in active development and allready in use in real world projects. So possible bugs & feature requests are being handled.
It provides administration features like "CreateUser","CreateGroup" etc. and also provides functions for Login, Token validation, etc.
For example creating a user is as easy as:
client := gocloak.NewClient("https://mycool.keycloak.instance")
token, err := client.LoginAdmin("user", "password", "realmName")
if err != nil {
panic("Something wrong with the credentials or url")
}
user := gocloak.User{
FirstName: "Bob",
LastName: "Uncle",
EMail: "something#really.wrong",
Enabled: true,
Username: "CoolGuy",
}
client.CreateUser(token.AccessToken, "realm", user)
if err != nil {
panic("Oh no!, failed to create user :(")
}
It does also supports Introspecting a Requesting Party Token
client := gocloak.NewClient(hostname)
token, err := client.LoginClient(clientid, clientSecret, realm)
if err != nil {
panic("Login failed:"+ err.Error())
}
rptResult, err := client.RetrospectToken(token.AccessToken, clientid, clientSecret, realm)
if err != nil {
panic("Inspection failed:"+ err.Error())
}
if !rptResult.Active {
panic("Token is not active")
}
permissions := rptResult.Permissions
//Do something with the permissions ;)
Also to handle easy authentication & token refresh when using echo there is another lib based on gocloak called gocloak-echo. This lib provides handler & middleware to help out, but is still in a more WIP state.
The library also provides decoding of accessTokens into custom claims
Disclosure: I am the (main) author of gocloak, so it's also a little advertising, but in general it answers the question. I had the same problem as the author and i decided to create my own lib (based on the lib of someone else, as stated in the readme on github).

How to install SSL certificate in Vapor web framework?

I want to install SSL(Comodo wildcard certificate, ex: "*.test.com")
in Vapor Web framework, the "servers.json" I got is:
{
"default": {
"port": "$PORT:443",
"host": "api.test.com",
"securityLayer": "tls",
"tls": {
"certificates": "chain",
"certificateFile": "/path/ssl-bundle.crt",
"chainFile": "/path/ssl-bundle.crt",
"privateKeyFile": "/path/key.pem",
"signature": "signedFile",
"caCertificateFile": "/path/AddTrustExternalCARoot.crt"
}
}
}
I already make sure that "public/private" key matches already using openssl command. And about the certificateFile part like "ssl-bundle.crt", I also tried "*.test.com.crt" with the "key.pem" as well(still pass the validation using openssl, the only difference is one is test.com's certificate, the other is bundle certificate, combined by correct orders already.). Besides, all certs and key's format are correct as well. And I also make sure the cert/key files location is correct so that the Vapor can find these files. But I still can't launch the server correctly, and always display the error.
I try to locate the exact location in xcode, but I can only see it fails in this method: "tls_accept_fds()", which is in tls_server.c of CLibreSSL library.
Also, I saw the error message the xcode displayed to me:
After use debug mode to trace, I can only know that it seems the program throws the error in "SSL_set_rfd()" or "SSL_set_rfd()", but I don't know exactly. The xcode only shows this to me, and I can't find any other error messages in the debug console. As result, so far I can only make sure that the error should be in this block:
int
tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write)
{
struct tls *conn_ctx = NULL;
// I pass this block
if ((ctx->flags & TLS_SERVER) == 0) {
tls_set_errorx(ctx, "not a server context");
goto err;
}
// I pass this block
if ((conn_ctx = tls_server_conn(ctx)) == NULL) {
tls_set_errorx(ctx, "connection context failure");
goto err;
}
// I pass this block
if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
tls_set_errorx(ctx, "ssl failure");
goto err;
}
// I pass this block
if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) {
tls_set_errorx(ctx, "ssl application data failure");
goto err;
}
// The error occurs here, in SSL_set_rfd or SSL_set_wfd, it will then go to err part: "*cctx = NULL;", not even go into the if block.
if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 ||
SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) {
tls_set_errorx(ctx, "ssl file descriptor failure");
goto err;
}
*cctx = conn_ctx;
return (0);
err:
tls_free(conn_ctx);
*cctx = NULL;
return (-1);
}
So, the above is all the info I got right now, and I can't find the solution on the internet for several days already...
Could anyone give me any hint about how to install SSL in Vapor web framework? I can correctly install the SSL in Apache, Nginx, Tomcat, etc already. But never success in Vapor, it seems like C library issue, but I don't know the real reason why it fails, thank you very much for any possible help.
The bug has been found and fixed here: https://github.com/vapor/tls/pull/27

GO https request, time did not serialize back to the original value

I'm starting to learn golang and I'm trying to make a simple http client that will get a list of virtual machines from one of our oVirt clusters. The API that I'm trying to access has a self-signed certificate (auto generated during the cluster installation) and golang's http.client encounters a problem when serializing the time from the certificate. Below you can find the code and the output.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"crypto/tls"
)
func do_request(url string) ([]byte, error) {
// ignore self signed certificates
transCfg := &http.Transport{
TLSClientConfig: &tls.Config {
InsecureSkipVerify: true,
},
}
// http client
client := &http.Client{Transport: transCfg}
// request with basic auth
req, _ := http.NewRequest("GET", url, nil)
req.SetBasicAuth("user","pass")
resp, err := client.Do(req)
// error?
if err != nil {
fmt.Printf("Error : %s", err)
return nil, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return []byte(body), nil
}
func main() {
body, _ := do_request("https://ovirt.example.com/")
fmt.Println("response Status:", string(body))
}
and the error when I'm trying to compile:
$ go run http-get.go
Error : Get https://ovirt.example.com/: tls: failed to parse certificate from server: asn1: time did not serialize back to the original value and may be invalid: given "141020123326+0000", but serialized as "141020123326Z"response Status:
Is there any way to ignore this verification? I tried making a request using other programming languages (python, ruby) and skipping insecure certificates seems to be enough.
Thank you!
PS: I know the proper solution is to change the certificate with a valid one, but for the moment I cannot do this.
Unfortunately, you've encountered an error that you cannot get around in Go. This is buried deep in the cypto/x509 and encoding/asn1 packages without a way to ignore. Specifically, asn1.parseUTCTime is expecting the time format to be "0601021504Z0700", but your server is sending "0601021504+0000". Technically, that is a known format but encoding/asn1 does not support it.
There are only 2 solutions that I can come up with that do not require a code change for golang.
1) Edit the encoding/asn1 package in your go src directory and then rebuild all the standard packages with go build -a
2) Create your own customer tls, x509 and asn1 packages to use the format your server is sending.
Hope this helps.
P.S. I've opened an issue with the Go developers to see if it can resolved by them at some later point Issue Link
Possible ASN1 UtcTime Formats.

Go Lang exec/spawn a ssh session

I'm trying to work out the mechanism to fork/start a ssh terminal session
i.e I want to be logged into remote server (my keys are on server) if I execute this program.
Right now it just executes but nothing happens.
package main
import (
"os/exec"
"os"
)
func main() {
cmd := exec.Command("ssh","root#SERVER-IP")
cmd.Stdout = os.Stdout
//cmd.Stderr = os.Stderr
cmd.Run()
}
cmd.Run waits for the command to complete. Your ssh session should (normally) not exit without user interaction. Therefore your program blocks, since it waits for the ssh process to finish.
You may want to either
also redirect Stdin, so you can interact with the ssh session
execute ssh me#server somecommand. In the last form a specific command gets executed and the output of this command gets redirected.
take a look at the ssh package
I've done library that can cover your requiremenets: https://github.com/shagabutdinov/shell; checkout if it helps or not.
You can use this library to start ssh session and execute the commands:
key, err := ssh.ParsePrivateKey([]byte(YOUR_PRIVATE_KEY))
if(err != nil) {
panic(err)
}
shell, err = shell.NewRemote(shell.RemoteConfig{
Host: "root#example.com:22",
Auth: []ssh.AuthMethod{ssh.PublicKeys(key)},
})
if(err != nil) {
panic(err)
}
shell.Run("cat /etc/hostname", func(_ int, message string) {
log.Println(message) // example.com
})
This is simple wrapper over golang ssh library that helps to execute consequent commands over /bin/sh.