Okay, so your Airflow task is getting stuck on requests.post()
when trying to grab a Keycloak token, even though it works fine locally and in a plain venv. This is a classic "it works on my machine" but not in Airflow!
Here's a rundown of what's likely going on and how to fix it, in a more Stack Overflow-friendly way:
It sounds like your Airflow task is hanging when it tries to make the requests.post()
call to Keycloak. This usually points to network issues from the Airflow worker, how the request itself is configured, or sometimes even Keycloak being picky.
Here’s what I’d check, step-by-step:
The BIG One: Timeouts! Your requests.post(...)
call doesn't have a timeout
. If the network is a bit slow or Keycloak takes a moment longer to respond from the Airflow worker, your request will just sit there waiting... forever.
Fix: Always add a timeout.
Python
try:
response = requests.post(
token_url,
data=token_data,
headers=headers,
timeout=(10, 30), # (connect_timeout_seconds, read_timeout_seconds)
verify=False # We'll talk about this next!
)
response.raise_for_status() # Good for catching 4xx/5xx errors
#... rest of your code
except requests.exceptions.Timeout:
logger.error("Request to Keycloak timed out!")
raise
except requests.exceptions.RequestException as e:
logger.error(f"Something went wrong with the request: {e}")
raise
Network Gremlins in the Airflow Worker: Your Airflow worker lives in a different environment than your local machine.
Firewalls/Security Groups: Can the worker actually reach your Keycloak server's address and port? Test this from inside the worker environment if you can (e.g., curl
from a worker pod if you're on Kubernetes).
Proxies: Does your Airflow environment need an HTTP/HTTPS proxy to get to the internet or your Keycloak instance? If so, requests
needs to be told about it (either via environment variables HTTP_PROXY
/HTTPS_PROXY
or the proxies
argument in requests.post
).
DNS: Can the worker resolve the Keycloak hostname? Again, test from the worker.
SSL Verification (verify=False
): You've got verify=False
. While this can bypass some SSL issues, it's a security risk (Man-in-the-Middle attacks). Sometimes, even with verify=False
, underlying SSL libraries can behave differently in different environments.
Better Fix: Get the CA certificate for your Keycloak instance and tell requests
to use it:
Python
response = requests.post(..., verify='/path/to/your/ca_bundle.pem')
Or set the REQUESTS_CA_BUNDLE
environment variable in your worker.
Debugging SSL: If you suspect SSL, you can try openssl s_client -connect your-keycloak-host:port
from the worker to see handshake details.
Keycloak Server Being Strict:
Is Keycloak configured to only allow requests from certain IPs? Your worker's IP might not be on the list. Check Keycloak's admin console and server logs.
Any weird client policies or rate limiting on the Keycloak side?
Python Environment Mismatch: Are the versions of requests
, urllib3
, or even Python itself the same in your Airflow worker as in your working venv? Subtle differences can cause issues.
requirements.txt
to ensure your Airflow environment is built consistently.Airflow Worker Resources: Less likely to cause a silent hang on requests.post
itself, but if the worker is starved for CPU or memory, things can get weird or very slow. Check your worker logs for any OOM errors.
Super-Detailed Logging (for desperate times): If you're really stuck, you can enable http.client
debug logging to see exactly what's happening at the HTTP level. It's very verbose, so use it sparingly.
Python
import http.client as http_client
import logging
http_client.HTTPConnection.debuglevel = 1
logging.basicConfig() # You need to initialize logging, otherwise you'll not see debug output.
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
Put this at the top of your task file.
Quick Summary of Code Improvements:
Python
import requests
import logging
from airflow.models import Variable
from urllib.parse import urljoin
import urllib3
logger = logging.getLogger("airflow.task") # Use Airflow's task logger
def get_keycloak_token() -> str:
# Consider if you really need to disable this warning. Best to fix SSL.
# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
keycloak_base_url = Variable.get("keycloak_base_url")
#... (get other variables)...
password = Variable.get("keycloak_password") # Ensure this isn't being double-JSON-encoded
token_url = urljoin(keycloak_base_url, f"/realms/{Variable.get('keycloak_realm')}/protocol/openid-connect/token")
token_data = {
'grant_type': 'password',
'username': Variable.get("keycloak_username"),
'password': password,
'client_id': Variable.get("keycloak_client_id"),
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
logger.info(f"Attempting to get token from: {token_url}")
try:
response = requests.post(
token_url,
data=token_data,
headers=headers,
timeout=(10, 30), # Connect, Read timeouts
# BEST: verify='/path/to/your/ca_bundle.pem'
# For now, if you must:
verify=False
)
response.raise_for_status() # Will raise an exception for 4XX/5XX errors
token_json = response.json()
access_token = token_json.get('access_token')
if not access_token:
logger.error("Access token not found in Keycloak response.")
raise ValueError("Access token not found")
logger.info("Successfully obtained Keycloak token.")
return access_token
except requests.exceptions.Timeout:
logger.error(f"Timeout connecting to Keycloak at {token_url}")
raise
except requests.exceptions.HTTPError as e:
logger.error(f"HTTP error from Keycloak: {e.response.status_code} - {e.response.text}")
raise
except requests.exceptions.ConnectionError as e:
logger.error(f"Connection error to Keycloak: {e}")
raise
except Exception as e:
logger.error(f"An unexpected error occurred while getting Keycloak token: {e}", exc_info=True)
raise
# Tip: Consider using Airflow Connections to store your Keycloak credentials
# instead of Variables for better security and management.
Start with the timeout. That's the most common culprit for this kind of hang. Then move on to network and SSL. Good luck!