Skip to content

Commit 73d13a2

Browse files
committed
Major refactors and docs updates.
1 parent 71d81c7 commit 73d13a2

File tree

2 files changed

+25
-116
lines changed

2 files changed

+25
-116
lines changed

README.md

Lines changed: 3 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11

22
# Tailscale Lambda Extension for Containers
33

4-
Inspired by [Corey Quinn](https://github.com/QuinnyPig) Tailscale [layer](https://github.com/QuinnyPig/tailscale-layer) project. This project aims to do the same thing but usable for those working with any Lambda Container runtime language.
5-
6-
This Tailscale extension has been modified from Corey's upstream work. We have removed verbose logging and event lifecycle hooks which just did some verbose logging.
4+
Inspired by Corey Quinn's [tailscale-layer](https://github.com/QuinnyPig/tailscale-layer) project. This project aims to do the same thing but usable for those working with any Lambda Container runtime language. Our extension has been modified from Corey's upstream work. I have removed verbose logging and event lifecycle hooks. This has reduced startup overhead, duplicate tailscale up calls, and more configuration options.
75

86
## Installation
97

@@ -31,7 +29,7 @@ We publish multi-platform images for both `linux/amd64` and `linux/arm64/v8` and
3129
- ghcr.io/rails-lambda/tailscale-extension-amzn
3230
- ghcr.io/rails-lambda/tailscale-extension-debian
3331

34-
## Usage
32+
## Example Usage
3533

3634
Once your Lambda function starts, you will have a [SOCKS5](https://en.wikipedia.org/wiki/SOCKS) proxy which can communicate with your Tailscale tailnet at `http://localhost:1055`. Here is an example of how to leverage that with Ruby's [socksify](https://github.com/astro/socksify-ruby) gem.
3735

@@ -41,85 +39,4 @@ Net::HTTP.socks_proxy('localhost', 1055).start(...) do |http|
4139
end
4240
```
4341

44-
Again, this extension is not coupled to any runtime language. So how you use the SOCKS5 proxy is up to you.
45-
46-
## Why? An Example!
47-
48-
I am currently using this extension to help develop our new LambdaCable gem which uses API Gateway's WebSockets. We reached the point where a faster feedback was required. So I use this proxy to forward all Lambda events to my local development.
49-
50-
In the example image below, the Tailscale Extension is used on the "Lambda Proxy" to send all HTTP and WebSocket event traffic to my local machine, illustrated below.
51-
52-
```mermaid
53-
flowchart TB
54-
%% Objects
55-
usr[/User/]
56-
subgraph aws["AWS Environment"]
57-
fun(Function URL)
58-
api(API Gateway)
59-
lam(Lambda Proxy)
60-
ddb[(DynamoDB)]:::node-org
61-
end
62-
subgraph local["Local Environment"]
63-
subgraph app["Application"]
64-
proxy(Proxy Server)
65-
end
66-
end
67-
%% Flow
68-
usr <--> |/*| fun
69-
usr <--> |/cable| api
70-
fun <--> lam
71-
api --> lam
72-
lam <--> |Tailscale| proxy
73-
app --> api
74-
app --> ddb
75-
%% Styles
76-
classDef node fill:#a99ff0,stroke:#fff,stroke-width:4px,color:#000;
77-
classDef node-wht fill:white,stroke:#ccc,stroke-width:1px,color:black;
78-
classDef node-org fill:#fdf3ea,stroke:#f7c296,stroke-width:2px,color:#000;
79-
classDef desc fill:white,stroke:#ccc,stroke-width:1px,color:black,font-size:12px;
80-
classDef node-sml font-size:13px,line-height:2em;
81-
classDef fs13 font-size:13px,line-height:1em;
82-
linkStyle 4 stroke:#f28add
83-
linkStyle 5 stroke:#f28add
84-
linkStyle 6 stroke:#f28add
85-
class aws node-wht
86-
class local node-wht
87-
```
88-
89-
If you are curious how all this worked with our Lamby gem and our WebSocket demo application, checkout these two pull requests for more details.
90-
91-
- Lamby Gem - [Local Development Proxy Server #164](https://github.com/rails-lambda/lamby/pull/164)
92-
- WebSocket Demo - [Live Development with Tailscale & Lamby #4
93-
](https://github.com/rails-lambda/websocket-demo/pull/4)
94-
95-
### Local Proxy Lessons
96-
97-
When running local development services to be used via a proxy, it is important that you use the `0.0.0.0` address to bind to all local interfaces vs the default of `127.0.0.1`. Doing so will ensure that [Tailscale Services](https://tailscale.com/kb/1100/services/) are updated and available. For Rails development server, use something like this:
98-
99-
```shell
100-
rails s -b 0.0.0.0
101-
```
102-
103-
[Dev Containers](https://containers.dev/) are an amazing technology spec used by great products such as [Codespaces](https://github.com/features/codespaces) and [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). If you are using them, you will need to ensure your Dev Container is forwarding the right ports. If your Dev Container is a single image (vs compose), then you can do this in the `devcotainer.json` file:
104-
105-
```json
106-
{
107-
"forwardPorts": [3000],
108-
}
109-
```
110-
111-
However, our Rails Lambda Dev Container leverages a `dockerComposeFile`, so you would also have to open that compose file and add this to your `devcontainer.json` matching `service` within the compose file. For example:
112-
113-
```yaml
114-
services:
115-
app:
116-
ports:
117-
- 3000:3000
118-
```
119-
120-
Here are a few helpful GitHub Issues I found on this topic.
121-
122-
- [Add support for devices in devcontainer-feature.json #153](https://github.com/devcontainers/spec/issues/153)
123-
- [Port forwarding issue in Containers #1009](https://github.com/microsoft/vscode-remote-release/issues/1009)
124-
- [Tailscale not detecting Docker Port Forwarding to Host #5813](https://github.com/tailscale/tailscale/issues/5813)
125-
42+
Again, this extension is not coupled to any runtime language. So how you use the SOCKS5 proxy is up to you. Enjoy!

src/tailscale.sh

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,60 +5,52 @@
55
set -euo pipefail
66
set +x
77

8+
# Configuration
89
OWN_FILENAME="$(basename "$0")"
910
LAMBDA_EXTENSION_NAME="$OWN_FILENAME" # (external) extension name has to match the filename
1011
TMPFILE="/tmp/tailscale.data"
1112

12-
# Graceful Shutdown
13-
_term() {
14-
echo "[${LAMBDA_EXTENSION_NAME}] Received SIGTERM"
15-
# forward SIGTERM to child procs and exit
16-
kill -TERM "$PID" 2>/dev/null
17-
echo "[${LAMBDA_EXTENSION_NAME}] Exiting"
18-
exit 0
19-
}
20-
21-
forward_sigterm_and_wait() {
22-
trap _term SIGTERM
23-
wait "$PID"
24-
trap - SIGTERM
25-
}
26-
2713
# Registration
2814
HEADERS="$(mktemp)"
2915
echo "[${LAMBDA_EXTENSION_NAME}] Registering at http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/register"
30-
curl -sS -LD "$HEADERS" -XPOST "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/register" --header "Lambda-Extension-Name: ${LAMBDA_EXTENSION_NAME}" -d "{ \"events\": [\"SHUTDOWN\", \"INVOKE\"]}" > $TMPFILE
16+
curl -sS -LD "$HEADERS" -XPOST "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/register" --header "Lambda-Extension-Name: ${LAMBDA_EXTENSION_NAME}" -d "{ \"events\": [\"SHUTDOWN\"]}" > $TMPFILE
3117

32-
RESPONSE=$(<$TMPFILE)
3318
# Extract Extension ID from response headers
19+
RESPONSE=$(<$TMPFILE)
3420
EXTENSION_ID=$(grep -Fi Lambda-Extension-Identifier "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
3521
echo "[${LAMBDA_EXTENSION_NAME}] Registration response: ${RESPONSE} with EXTENSION_ID $(grep -Fi Lambda-Extension-Identifier "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)"
3622

3723
# Start the Tailscale process
24+
echo "[${LAMBDA_EXTENSION_NAME}] Tailscale process..." 1>&2;
25+
/opt/bin/tailscaled --tun=userspace-networking --socks5-server=localhost:1055 --socket=/tmp/tailscale.sock --state=/tmp/tailscale &
26+
TAILSCALED_PID=$!
27+
echo "[${LAMBDA_EXTENSION_NAME}] TAILSCALED_PID: ${TAILSCALED_PID}" 1>&2;
28+
sleep 1
29+
30+
# Tailscale up
31+
echo "[${LAMBDA_EXTENSION_NAME}] Tailscale up..." 1>&2;
3832
TS_HOSTNAME=${TS_HOSTNAME:-lambda}
39-
if [ -n "${AWS_LAMBDA_FUNCTION_VERSION:-}" ]; then
33+
if [ "${AWS_LAMBDA_FUNCTION_VERSION}" != '$LATEST' ]; then
4034
TS_HOSTNAME="${TS_HOSTNAME}-v${AWS_LAMBDA_FUNCTION_VERSION}"
4135
fi
42-
/opt/bin/tailscaled --tun=userspace-networking --socks5-server=localhost:1055 --socket=/tmp/tailscale.sock --state=/tmp/tailscale & sleep 2
4336
until /opt/bin/tailscale --socket=/tmp/tailscale.sock up --authkey="${TS_KEY}" --hostname="${TS_HOSTNAME}"
4437
do
4538
sleep 0.1
4639
done
40+
sleep 1
4741

48-
# Event processing
42+
# Waiting for SHUTDOWN event.
4943
while true
5044
do
5145
echo "[${LAMBDA_EXTENSION_NAME}] Waiting for event. Get /next event from http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/event/next"
5246
# Get an event. The HTTP request will block until one is received
53-
curl -sS -L -XGET "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/event/next" --header "Lambda-Extension-Identifier: ${EXTENSION_ID}" > $TMPFILE &
54-
PID=$!
55-
forward_sigterm_and_wait
47+
curl -sS -L -XGET "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/event/next" --header "Lambda-Extension-Identifier: ${EXTENSION_ID}" > $TMPFILE
48+
echo "[${LAMBDA_EXTENSION_NAME}] Event received. Processing..." 1>&2;
5649
EVENT_DATA=$(<$TMPFILE)
57-
if [[ $EVENT_DATA == *"SHUTDOWN"* ]]; then
58-
echo "[extension: ${LAMBDA_EXTENSION_NAME}] Received SHUTDOWN event. Exiting." 1>&2;
59-
# Cleanly shut down the Tailscale process
60-
/opt/bin/tailscale --socket=/tmp/tailscale.sock down
61-
exit 0 # Exit if we receive a SHUTDOWN event
62-
fi
63-
sleep 1
50+
cat $EVENT_DATA 1>&2;
51+
echo "[${LAMBDA_EXTENSION_NAME}] Calling tailscale down..." 1>&2;
52+
/opt/bin/tailscale --socket=/tmp/tailscale.sock down
53+
echo "[${LAMBDA_EXTENSION_NAME}] Sending term to ${TAILSCALED_PID}..." 1>&2;
54+
kill -TERM "$TAILSCALED_PID" 2>/dev/null
55+
exit 0
6456
done

0 commit comments

Comments
 (0)