Skip to main content

Tailscale Lambda Extension

npm version PyPI version

A CDK construct that creates an AWS Lambda Layer containing Tailscale binaries, enabling Lambda functions to connect to your Tailscale network.

Available in CDK as a TypeScript NPM Package and Python PyPi Package.

Can be used with ALL AWS Lambda runtimes(Node, Python, Go, etc.) running on Amazon Linux 2023.

Installation

npm install tailscale-lambda-extension

Usage

The Lambda function using this layer requires the following Environment Variables:

  • TS_SECRET_API_KEY - The name of the AWS Secrets Manager secret that contains the pure text Tailscale API Key (auth key or OAuth client key).
  • TS_HOSTNAME - The "Machine" name as shown in the Tailscale admin console that identifies the Lambda function.

Optional Environment Variables:

  • TS_ADVERTISE_TAGS - Tags to advertise during tailscale up (e.g., tag:aws). Required when using OAuth client keys instead of auth keys.
  • TS_EXIT_NODE - Exit node hostname or IP address. When set, internet-bound traffic is routed through the specified exit node. The extension waits up to 10 seconds for the exit node to become reachable.
  • TS_EXIT_NODE_REQUIRED - Set to true to abort the extension if the exit node is not reachable within 10 seconds (only the literal value true is recognized, case-insensitive). Defaults to false (warn and continue).
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import { TailscaleLambdaExtension } from 'tailscale-lambda-extension';

export class MyStack extends cdk.Stack {

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Define the layer
    const tailscaleExtension = new TailscaleLambdaExtension(this, 'TailscaleExtension');

    // Add the layer to your Lambda function
    const myLambda = new NodejsFunction(this, 'MyFunction', {
      runtime: lambda.Runtime.NODEJS_20_X,
      entry: "/path/to/my/file.ts",
      handler: 'index.handler',
      layers: [tailscaleExtension.layer],
      environment: {
        TS_SECRET_API_KEY: "tailscale-api-key",
        TS_HOSTNAME: "my-lambda",
        // Optional: advertise tags (required for OAuth client keys)
        TS_ADVERTISE_TAGS: "tag:aws",
        // Optional: route internet-bound traffic through an exit node
        TS_EXIT_NODE: "100.x.y.z",
      }
    });

    // Give the Lambda and thus the Extension permission to read the Tailscale API Key Secret from Secrets Manager
    const tsApiKeySecret = secretsmanager.Secret.fromSecretNameV2(this, "tailscale-api-key", "tailscale-api-key");
    tsApiKeySecret.grantRead(myLambda);
  }

}

Accessing your Tailscale Network within the Lambda

The Tailscale process exposes a local SOCKS5 proxy on port 1055. You can use this proxy in your AWS runtime to route traffic through your Tailscale network. Here is an example of how it can be done with the socks-proxy-agent package and native http package in a TS Node.js function:

npm install socks-proxy-agent aws-lambda
import http from 'http';
import { SocksProxyAgent } from 'socks-proxy-agent';
import { APIGatewayProxyResultV2 } from 'aws-lambda';

// Helper Function wrapping the http request and returning the response in known APIGatewayProxyResultV2
async function proxyHttpRequest(
  target: Pick<http.RequestOptions, "hostname" | "port" | "agent">,
  request: {
    path:  string,
    method: string,
    headers: Record<string, string>,
    body: string | undefined,
  }
): Promise<APIGatewayProxyResultV2> {
  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    const apiRequest = http.request({
      ...target,
      path: request.path,
      method: request.method,
      headers: request.headers,
    }, (res: http.IncomingMessage) => {
      res.on('data', (chunk: Buffer) => {
        chunks.push(chunk);
      });
      res.on('end', () => {
        const responseBody = Buffer.concat(chunks);
        resolve({
          statusCode: res.statusCode || 500,
          headers: res.headers as Record<string, string>,
          body: responseBody.toString('base64'),
          isBase64Encoded: true,
        });
      });
      res.on('error', (error: Error): void => {
        console.error('Error receiving response:', error);
        reject(error);
      });
    });

    apiRequest.on('error', (error: Error): void => {
      console.error('Error sending request:', error);
      reject(error);
    });

    if (request.body != null) {
      apiRequest.write(request.body);
    }
    apiRequest.end();
  });
}

export async function handler() {

  const socksProxyAgent = new SocksProxyAgent('socks://localhost:1055');

  const response = await proxyHttpRequest({
      hostname: "Target IP Address that is connected to the TailScale network",
      port: "Target IP Address that is connected to the TailScale network",
      agent: socksProxyAgent,
    }, {
      path: "/test",
      headers: {
        'Content-Type': 'application/json',
      },
      method: "POST",
      body: {
        "test": "data"
      },
    }
  );

}

Tailscale Configuration

Tags

Tags are created for access control and to enable certain features on Auth Key, we are specifically interested in the fact that if you tag an Auth Key, then that Auth Key will not expire. Create a tag for our Lambdas.

  1. Go to your Tailscale network Access Control: https://login.tailscale.com/admin/acls/file
  2. Add the tag to the top of the file in the correct property:
{
    ...
	"tagOwners": {
		"tag:aws": [],
	},
    ...
}
  1. Save the file.

OAuth client keys do not expire, eliminating the need for manual key rotation. They require --advertise-tags to be passed during tailscale up.

  1. Go to your Tailscale network, Settings, OAuth clients: https://login.tailscale.com/admin/settings/trust-credentials
  2. Click "Generate OAuth client..."
  3. Grant the devices:core, auth_keys scopes
  4. Add the tag:aws tag
  5. Copy the client secret to your AWS Secrets Manager secret
  6. Set the TS_ADVERTISE_TAGS environment variable on your Lambda to tag:aws

Exit Nodes

To route Lambda internet-bound traffic through a Tailscale exit node:

  1. Ensure you have an exit node configured in your Tailscale network
  2. Set the TS_EXIT_NODE environment variable on your Lambda to the exit node's Tailscale IP (e.g., 100.x.y.z)
  3. The extension will configure the exit node during initialization and wait up to 10 seconds for it to become reachable
  4. By default, if the exit node is not reachable in time, the extension logs a warning and continues (fail-open)
  5. Set TS_EXIT_NODE_REQUIRED=true to abort the extension if the exit node is not reachable (fail-closed) — recommended when exit node routing is critical (e.g., residential IP requirement)

Auth Keys

Create a new Key on Tailscale:

  1. Go to your Tailscale network, Settings, Keys https://login.tailscale.com/admin/settings/keys
  2. Click "Generate auth key...".
  3. Select Reusable
  4. Select Ephemeral
  5. Select Tags then Add the tag:aws tag.
  6. Click on "Generate key" and copy the Auth Key to your Tailscale AWS Secrets Manager Secret (create it manually if it does not exist, then place the Secret name in the TS_SECRET_API_KEY environment variable).

Compatibility

  • Compatible with ALL AWS Lambda runtimes running on Amazon Linux 2023
  • Only Supports x86_64 architecture

Limitations

  • The IP address of the Tailscale target must be used, Domain Name resolution is not set up. This is not too much of a limitation/risk as the IP address of the target server can be changed from the Tailscale Admin Console if need be.
  • The Layer adds about 50MB to the Lambda package size, most of which is the Tailscale binaries.
  • Expect a 5-10 second addition to your cold start time due to the Tailscale process starting up.

Implementation Details

The extension is build using the following steps:

  1. Uses Docker to build the binaries in an Amazon Linux 2023 environment
  2. Installs Tailscale from the official repository
  3. Packages the binaries into a Lambda Layer
  4. Starts the Tailscale process as an External Extension, allowing the main Runtime process to communicate with the Tailscale process over a local SOCKS5 proxy on port 1055.

Tailscale Version

The current version is v1.96.4

Resources used

Credit to the following resources for providing the necessary information to build this extension:

License

Apache-2.0

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Metadata

Release files for tailscale-lambda-extension 0.0.11

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tailscale-lambda-extension 0.0.11
File Size Uploaded
tailscale_lambda_extension-0.0.11.tar.gz 36.3 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for tailscale-lambda-extension 0.0.11
File Interpreter ABI Platform
tailscale_lambda_extension-0.0.11-py3-none-any.whl Python 3 none any Details

Total release size: 72.6 MB

Release files / tailscale_lambda_extension-0.0.11.tar.gz

Download URL tailscale_lambda_extension-0.0.11.tar.gz
Size 36.3 MB
Tags Source
SHA-256 checksum
How to use checksums
8d5a68b36c86ef26524b60248d1fbea2f5a282d8f21a07b921a207d7d8b55719
BLAKE2b-256 checksum
How to use checksums
92a3088d30050f8f070e57724aaa07f19550783838e403d37fdf1d63d088f7ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.14.4

Release files / tailscale_lambda_extension-0.0.11-py3-none-any.whl

Download URL tailscale_lambda_extension-0.0.11-py3-none-any.whl
Size 36.3 MB
Tags Python 3
SHA-256 checksum
How to use checksums
f8868d4c2ba52e5d092c90e89ee54f528d1124cfeb4920b0aff93883936cdb59
BLAKE2b-256 checksum
How to use checksums
591f428308f39c20c4d80daac5ab27e7c6b6fd078e4d4ee5ebbdc38223c96faf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.0.11 This release

2 release files

0.0.10

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page