Skip to content

Instantly share code, notes, and snippets.

View tylertreat's full-sized avatar

Tyler Treat tylertreat

View GitHub Profile
@tylertreat
tylertreat / web.xml
Created October 10, 2019 15:02
App Engine Spring servlet config
<?xml version="1.0" encoding="utf-8"?>
<web-app version="3.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<servlet>
<servlet-name>appengine-spring-boot</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
@tylertreat
tylertreat / oidc_cloud_function.py
Last active September 16, 2019 18:26
Example Cloud Function that makes authenticated requests to an IAP-protected resource
import os
import google.auth
import google.oauth2.service_account
from google.auth.transport.requests import Request
import requests
IAM_SCOPE = 'https://www.googleapis.com/auth/iam'
OAUTH_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
@tylertreat
tylertreat / main.py
Last active January 29, 2019 06:52
GCP OIDC proxy credentials signer
_adc_credentials, _ = google.auth.default(scopes=[IAM_SCOPE])
# For service accounts using the Compute Engine metadata service, which is the
# case for Cloud Function service accounts, service_account_email isn't
# available until refresh is called.
_adc_credentials.refresh(GRequest())
# Since the Compute Engine metadata service doesn't expose the service
# account key, we use the IAM signBlob API to sign instead. In order for this
# to work, the Cloud Function's service account needs the "Service Account
@tylertreat
tylertreat / main.py
Last active January 29, 2019 06:52
GCP OIDC proxy whitelist
# Check path against whitelist.
path = proxied_request.path
if not path:
path = '/'
# TODO: Implement proper wildcarding for paths.
if '*' not in _whitelist and path not in _whitelist:
logging.warn('Rejected {} {}, not in whitelist'.format(
proxied_request.method, url))
return 'Requested path {} not in whitelist'.format(path), 403
@tylertreat
tylertreat / main.py
Last active January 29, 2019 06:49
GCP OIDC proxy response
# Strip hop-by-hop headers and Content-Encoding.
headers = _strip_hop_by_hop_headers(resp.headers)
headers.pop('Content-Encoding', None)
return resp.content, resp.status_code, headers.items()
@tylertreat
tylertreat / main.py
Last active January 29, 2019 06:51
GCP OIDC proxy send request
global _oidc_token
if not _oidc_token or _oidc_token.is_expired():
_oidc_token = _get_google_oidc_token()
logging.info('Renewed OIDC bearer token for {}'.format(
_adc_credentials.service_account_email))
# Add the Authorization header with the OIDC token.
headers['Authorization'] = 'Bearer {}'.format(_oidc_token)
# We don't want to forward the Host header.
@tylertreat
tylertreat / main.py
Last active January 29, 2019 04:30
GCP OIDC proxy get Google OIDC token
class OIDCToken(object):
def __init__(self, token_str):
self._token_str = token_str
self._claims = jwt.decode(token_str, verify=False)
def __str__(self):
return self._token_str
def is_expired(self):
@tylertreat
tylertreat / main.py
Last active January 29, 2019 06:51
GCP OIDC proxy URL creation portion of handle_request
def handle_request(proxied_request):
"""Proxy the given request to the URL in the Forward-Host header with an
Authorization header set using an OIDC bearer token for the Cloud
Function's service account. If the header is not present, return a 400
error.
"""
host = proxied_request.headers.get(HOST_HEADER)
if not host:
return 'Required header {} not present'.format(HOST_HEADER), 400
@tylertreat
tylertreat / GCPAuthenticationInterceptor.java
Created January 25, 2019 17:08
Method to exchange JWT for Google-signed OIDC token
private DecodedJWT getGoogleIdToken() throws IOException {
String jwt = getSignedJwt();
final GenericData tokenRequest = new GenericData()
.set("grant_type", JWT_BEARER_TOKEN_GRANT_TYPE)
.set("assertion", jwt);
final UrlEncodedContent content = new UrlEncodedContent(tokenRequest);
final HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
final HttpRequest request = requestFactory
@tylertreat
tylertreat / GCPAuthenticationInterceptor.java
Created January 25, 2019 16:48
Method to get a JWT signed by a GCP service account for OIDC
private String getSignedJwt() {
long now = System.currentTimeMillis();
RSAPrivateKey privateKey = (RSAPrivateKey) credentials.getPrivateKey();
Algorithm algorithm = Algorithm.RSA256(null, privateKey);
return JWT.create()
.withKeyId(credentials.getPrivateKeyId())
.withIssuer(credentials.getClientEmail())
.withSubject(credentials.getClientEmail())
.withAudience(OAUTH_TOKEN_URI)
.withIssuedAt(new Date(now))