The first step when interacting with the Nutanix Rest API with GO is to authenticate against the API. To do so you need to know that Nutanix makes use of user authentication. There is a role concept for users, which means a user can have different rights when connecting.
There are three roles a user can be assigned to:
- Viewer: This role allows a user to view information only. It does not provide permission to perform any administrative tasks.
- Cluster Admin: This role allows a user to view information and perform any administrative task (but not create or modify user accounts).
- User Admin: This role allows the user to view information, perform any administrative task, and create or modify user accounts.
The first user in PRISM (GUI) which is direct related to the API authentication is “admin” with the default password “admin” which will be changed via the first connect to PRISM. I prefer to set the admin password to the default password “nutanix/4u” in my test environments but this is up to you. The admin user does have all three roles assigned.
Using Google Chrome developer tools to learn how PRISM authentication works (optional)
At the moment the documentation of the REST API lacks some easy examples and explanations how the authentication should be done. I started to learn this by making use of the Google Chrome developer tools when I am connecting to Nutanix via the Web GUI.
First step is to open Google Chrome and show the developer tools and switch to the Network tab. Clear all and start the recording. Type in CVM or Cluster IP/DNS to open the PRISM GUI.
There are two important URL the browser requested:
- https://192.168.178.130:9440/PrismGateway/services/rest/v1/users/session_info
- It looks like that the client tries to check if we are already connected which is this case fails because the response code shows “401” which means we are not authorized to access this URL at the moment.
- https://192.168.178.130:9440/PrismGateway/services/rest/v1/utils/pre_login_details
- After the client understood that this session is not authorized it ask to get some details before a login will take place.
- What is good to know that a response looks like this which gives a lot of details which helps to identify what API version should be supported etc.: (without authentication!)
{ "welcomeBanner":[ ], "ff":[ 7, 4, 6, 9, 2, 1 ], "clusterId":"2428284760558904295", "clusterUuid":"000536d0-f06d-323f-21b3-001fc69ba3e7", "clusterName":"NUC6i7KYK", "applianceVersion":{ "buildName":"el6-release-ce-2016.09.23-stable-c35dedf85de1e0698458a5ede4a54faee9f45c05", "buildType":"release", "version":"ce-2016.09.23-stable", "commitId":"c35dedf85de1e0698458a5ede4a54faee9f45c05", "commitIdShort":"c35ded", "lastCommitDate":"2016-09-23 09:37:09 -0700" }, "multicluster":false, "ceCluster":true, "changeSystemDefaultPassword":false, "upgradeInProgress":false, "clusterConversionInProgress":false, "ceUser":{ "username":"thomas.findelkind@nutanix.com", "firstName":"Thomas", "middleInitial":"", "lastName":"Findelkind", "emailId":"thomas.findelkind@nutanix.com", "ceEnabled":true, "validCredentials":true, "lastLoginAttemptTimeInUsecs":1467646879586000, "lastSuccessfulLoginTimeInUsecs":1479076500714000, "failedLoginDurationInUsecs":1479115508249000, "reasonForFailure":"" }, "local":true }
Let’s type in the user and password now and stop the recording ones we are successfully connected to the GUI. You should found a POST to https://192.168.178.130:9440/PrismGateway/j_spring_security_check. There are three interesting parts:
- It is posting the username and password in Form data as j_username and j_password
- It is receiving the Set-Cookie: … in the Response header which means a cookie can be used for all subsequent http methods
-
It is receiving the Location: https://192.168.178.130:9440/PrismGateway/nmb/loginsuccess which is like a redirection in this case
The https://192.168.178.130:9440/PrismGateway/nmb/loginsuccess GET will be requested via the client with the cookie which seems to check if it works. A response of the status code of “200” and the response of “Success” means cookie authentication is working. Then a https://192.168.178.130:9440/PrismGateway/services/rest/v1/users/session_info is followed which gets some user session info like userDTO.
I believe the rest can be ignored for our task now. We learned this basic workflow for authentication.
- Request session_info to check if we are already authenticated
- Request pre_login_details which may be used to react on different API versions
- Send username and password with the request
- Set or receive a cookie for subsequent http methods
- check if we are connected via loginsuccess
Nutanix REST API Authentication with GO
There are two methods to authenticate to the Nutanix REST API.
1. Basic Authentication :
The user provides user-id and password every time a request is send as the auth-header.
2. Session Authentication :
The user credentials are stored in a cookie.
GO Authenticate via “Basic Authentication”
I wrote an example which shows how the basic authentication works. The general workflow is simple. EVERY http method sends the username and password in a encoded fashion. Nutanix REST API requires a base64 encondig which is included in the “encoding/base64” package.
I created the func EncodeCredentials which encodes the input parameter username and password as required.
// EncodeCredentials this func is encoding the Username and Password with base64 encoding which is // required for Nutanix func EncodeCredentials(username string, password string) string { return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) }
The next two functions return the API entry points for v0.8 and v1.0 of the Nutanix API. It uses the NutanixHost parameter to generate the full https request string. New API version v2.0 and v3.0 will be introduced soon.
// v0_8 returns the main entry point for the v0.8 Nutanix API func v0_8(NutanixHost string) string { return "https://" + NutanixHost + ":9440/api/nutanix/v0.8/" } // v1_0 returns the main entry point for the v1.0 Nutanix API func v1_0(NutanixHost string) string { return "https://" + NutanixHost + ":9440/PrismGateway/services/rest/v1/" }
For testing purposes I set some variables to meet my test environment in the main function. Feel free to change this to your settings.
// PRISM user var username = "admin" // PRISM user password var password = "nutanix/4u" // Nutanix Cluster IP/DNSName CVM IP/DNSName var NutanixHost = "192.168.178.130"
The next part is only required if a certificate at the Nutanix cluster is used which is self-signed. Basically this ignores that this certificate can not be validated.
// Ignores certificates which can not be validated tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
A http client will be created which leverage the Transport “tr” we just defined.
// create a HTTP client var httpClient = http.Client{Transport: tr}
Now we are able to create the GET request to session_info at https://192.168.178.130:9440/PrismGateway/services/rest/v1//users/session_info. This is inline with the PRISM GUI workflow.
// create a http Request pointer var req *http.Request var err error // Defines the HTTP Request // send a GET to the NUTANIX API and receives the user session_info // https://192.168.178.130:9440/PrismGateway/services/rest/v1//users/session_info req, err = http.NewRequest("GET", v1_0("192.168.178.130")+"/users/session_info", nil)
Before we send the request we make sure username and password will be included in the request header. The key we need to set is “Authorization” with a defined base64 value with isa string of “Basic “+encodedString.
// before the request is send set the HTTP Header key "Authorization" with // the value of base64 encoded Username and Password req.Header.Set("Authorization", "Basic "+EncodeCredentials(username, password))
The request can be send and we are able to handle the response. In this example I am checking for some response codes but it is up to you to implement more.
resp, err := httpClient.Do(req) if err != nil { log.Fatal(err) os.Exit(1) } defer resp.Body.Close() // Status Code 401 Unauthorized means user+password was not valid // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes if resp.StatusCode == 401 { log.Fatal("Username or password not valid for host: " + NutanixHost) os.Exit(1) } // Response status code 200 should be send if credentials are valid // all other could be ignored or handle if needed if resp.StatusCode != 200 { log.Fatal("Connection to host: " + NutanixHost + " not possible") os.Exit(1) }
The last part prints the response body to give you a feedback and the received info about the user.
// read the data from the resp.body into htmlData htmlData, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) os.Exit(1) } // print the response body (htmlData) to give you a feedback fmt.Println(string(htmlData))
That’s it. Just remember you only need to make sure the header of the request includes the base64 encoded string.
GO Authenticate via “Session Authentication”
I wrote an example which shows how the session authentication works. The general workflow is like that. Send a “basic authentication” http Get with base64 encoded credentials and set a cookie. Use the cookie in all subsequent http methods .
I will only focus on the parts which change to the “basic authentication” part.
The way how the HTTP client will be created changed. First the cookie will be created and the the new created http client is using this cookie.
cookieJar, _ := cookiejar.New(nil) // create a HTTP client var httpClient = http.Client{Transport: tr, Jar: cookieJar}
There is a second request but this time we don’t set the authorization header. The reason is simple. The http client makes sure the cookie is send with the encrypted credentials.
// Defines the 2. HTTP Request without setting the "Authorization" header // send a GET to the NUTANIX API and receives the user session_info // https://192.168.178.130:9440/PrismGateway/services/rest/v1/users/session_info req, err = http.NewRequest("GET", v1_0("192.168.178.130")+"/users/session_info", nil) resp, err = httpClient.Do(req) if err != nil { log.Fatal(err) os.Exit(1) } defer resp.Body.Close()
Done.
I found two blogs which helped me to learn about this:
and
The post Nutanix REST API with Golang/Go Tutorial – Part 2 – Authentication appeared first on tfindelkind.com.