Access Google Cloud IAP-Protected resources via CLI

Sandeep Agarwal
Google Cloud - Community
4 min readJul 29, 2024

I’ve always known network security and identity management to be separate domains. Like those awkward cousins at a family reunion — they knew each other existed, but rarely spoke.

Until I discovered Google Cloud’s Identity Aware Proxy or IAP.

IAP provides an identity-based firewall to protect applications. It’s like giving your firewall a superpower — the ability to recognize who’s knocking before opening the door!

Image source: Google Cloud

Setting up IAP for web clients (access via the browser) is a breeze. But if you have APIs that users need to access via the command line interface (CLI), the setup can be tricky. This blog simplifies the steps to do that.

Creating the API

First, let’s deploy a simple API on AppEngine that our users need to access.

Create the following files in a directory using your favorite editor.

mkdir iap-test && cd iap-test

requirements.txt

Flask==3.0.0

main.py

import flask
import json

app = flask.Flask(__name__)

@app.get("/")
def hello():
return {
'statusCode': 200,
'body': json.dumps('Function Succeeded!')
}

if __name__ == "__main__":
app.run(host="localhost", port=8080, debug=True)

app.yaml

runtime: python312
service: iap-test

Let’s deploy this to AppEngine via the gcloud command.

gcloud app deploy

On successful deployment, my test app is live at https://iap-test-dot-pensande.el.r.appspot.com/

Secure the API using IAP

Go to the IAP console and enable IAP on the AppEngine app. Details here.

Enable IAP on the App Engine application

Now grant the IAP-secured Web App User role to your user account.

Finally, go to the Credentials console and create an OAuth 2.0 Client of type Desktop app and give it a name. Once created, download the OAuth Client credentials and store it locally as a client_secret.jsonfile.

Create an OAuth 2.0 Client of type Desktop app

Create the Local CLI Application

Now create two files requirements.txt and secure-desktop-cli.py using your favorite editor.

requirements.txt

google-auth-oauthlib
keyrings.cryptfile
keyring

secure-desktop-cli.py

from google_auth_oauthlib.flow import InstalledAppFlow
import requests
import keyring
import getpass

SCOPES = [
'openid',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/cloud-platform'
]

service_name = 'iap-desktop-cli'
user_name = getpass.getuser()
url = input("Enter your api: ")

def fetch_token():
# use the client secret to fetch user creds
flow = InstalledAppFlow.from_client_secrets_file('client_secret.json', SCOPES)
creds = flow.run_local_server(host='localhost', port=4545)
token = creds.id_token
keyring.set_password(service_name, username=user_name, password=token)
return token

def fetch_response(token):
# use the user creds to fetch the api response
headers = {
"Authorization": "Bearer %s" %token,
}
response = requests.get(url,headers=headers)

if response.status_code == 200:
print(response.json())
elif response.status_code == 401:
token = fetch_token()
fetch_response(token)
else:
print(f"Error code: ${response.status_code}")

try:
token = keyring.get_password(service_name, user_name)
if token is None:
token = fetch_token()
fetch_response(token)
except Exception as e:
print(e)

Notice that we are using a non-standard port (4545) on the localhost to listen for the authorization code returned by the authorization server.

Notice also how we are storing the credentials token in a local vault enabled by keyrings.cryptfile — an encrypted text file storage. For production usage, you should replace it with a keyring backend that uses an OS-native vault such as the macOS Keychain or a Linux or Windows equivalent.

Test and Verify

Let’s set up a python virtual environment to install dependencies and run the code.

sudo apt install python3.11-venv
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python3 secure-desktop-cli.py

If everything is set up correctly, you’ll be prompted for the app-engine URL and your vault password. If you are doing this for the first time, a browser window pops up where you are prompted to authenticate (if you are not already logged in with an active session) and then grant consent to the App.

Finally, the JSON output is printed on your terminal. \o/

CLI flow for first interaction (token not present or invalid)
CLI flow for subsequent interactions (token present and valid)

Securing the Client Credentials

A security nerd would be skeptic about the above implementation as installed apps (e.g. mobile, desktop and single-page apps) cannot securely store client credentials. This is where the Proof Key for Code Exchange (PKCE) protocol comes in to enhance the security of the installed app flow.

Simply by enabling the autogenerate_code_verifierflag, the google-auth SDK generates a random, high-entropy string called code_verifier for every authorization request. Its transformed value, called code_challenge, is sent to the authorization server to obtain the auth_code.

The SDK then sends the auth_code, the code_verifier, and client credentials to the token endpoint which verifies the code_verifier against the previously submitted code_challenge before issuing a token.

Even if a malicious actor were to steal your client credentials and intercept an auth_code, it wouldn’t be able to exchange it for a token, as they would not have the code_verifier.

So we modify our InstalledAppFlow call as follows.

flow = InstalledAppFlow.from_client_secrets_file(
'client_secret.json',
scopes=SCOPES,
autogenerate_code_verifier=True
)

When we re-run our CLI app, notice how the authorization URL below includes the code_challenge and code_challenge_method query parameters.

CLI flow for PKCE-enabled interactions

--

--

Google Cloud - Community
Google Cloud - Community

Published in Google Cloud - Community

A collection of technical articles and blogs published or curated by Google Cloud Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

Sandeep Agarwal
Sandeep Agarwal

Written by Sandeep Agarwal

Sandeep is a Security Specialist with Google Cloud. He is passionate about evangelizing the security, risk and compliance benefits of cloud computing.