containerendpoint

package
v0.0.1-alpha.37 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package containerendpoint keeps AWS resource URLs dialable from inside the containers Overcast starts — Lambda functions, ECS tasks — which are siblings of the Overcast container rather than children of it.

Why this is needed at all: several AWS SDKs resolve a service endpoint from a resource URL instead of from client configuration. SQS is the sharp edge — @aws-sdk/middleware-sdk-sqs replaces the resolved endpoint with the QueueUrl's origin whenever the two differ and the client was not constructed with an explicit `endpoint`, and AWS_ENDPOINT_URL does not count because it resolves through the endpoint ruleset's Endpoint parameter, never as config.endpoint. .NET and Java v1 go further and use the queue URL as the request URI. So setting AWS_ENDPOINT_URL on the container is not sufficient: a queue URL carrying "localhost:4566" sends the container's SQS client to the container's own loopback.

Two mechanisms cover the two ways a URL reaches a container:

  • Minted at runtime by the container itself (CreateQueue, GetQueueUrl, ListQueues) — handled server-side, see internal/middleware.
  • Baked into the container environment by a host-side deploy (CDK/ CloudFormation writing queue.queueUrl into env) — handled here, by Mapper.RewriteURLs for loopback origins and Mapper.ExtraHosts for split-horizon hostnames.

Index

Constants

View Source
const ContainerCABundlePath = "/opt/overcast/ca.pem"

ContainerCABundlePath is where the CA bundle lands inside a container. Under /opt so it coexists with Lambda layer content, which is copied to the same tree.

Variables

This section is empty.

Functions

func BaseURL

func BaseURL(cfg *config.Config, host string) string

BaseURL is the origin containers use to reach Overcast on host — the single place the container-facing scheme is decided, shared by the Lambda and ECS runtimes. The scheme follows the server's listener: with TLS on, an http endpoint is not a degraded mode, it is a hard failure.

func Hostnames

func Hostnames(cfg *config.Config) []string

Hostnames returns every name Overcast may have minted a URL under, filtered to those a container can usefully be told about: no blanks, no IP literals (already addresses), and no loopback names, which name the container itself.

Shared by ExtraHosts and by the DNS zone (internal/dns), so the exact-match /etc/hosts entries and the wildcard resolver claim the same set. They were separate lists once and drifted.

func NativeLinuxDaemon

func NativeLinuxDaemon(ctx context.Context, dc *docker.Client) bool

NativeLinuxDaemon reports whether the Docker daemon appears to run on this same kernel — true on a native Linux daemon, false when it does not (including Docker Desktop, whose daemon lives inside a VM this process cannot bind a gateway address on) or when the fact cannot be established at all (no client, no "bridge" network to inspect).

It answers the same question gateway mode vs. host mode answers in resolveListen — is a bridge network's gateway an address this kernel can bind — but against Docker's own default network rather than one Overcast manages: the fact under test (whose kernel is this) does not depend on which network is asked about, and "bridge" is guaranteed to exist already, unlike a plane a caller is still deciding whether to create `--internal`.

func PublishedPort

func PublishedPort(ctx context.Context, dc inspectClient, internalPort int, logger *zap.Logger) int

PublishedPort returns the host port that internalPort is published on when Overcast itself runs in a container, or 0 when the mapping cannot be determined: the process is not containerised, the Docker socket is not mounted, or the port is not published at all.

Returning 0 means "no better answer than the one you already have" — callers keep their configured port. A native binary is never containerised, so it never reaches the Docker call and its behaviour is unchanged.

func Resolve

func Resolve(ctx context.Context, dc networkClient, network string, port int, logger *zap.Logger) string

Resolve returns the origin ("http://host:port") that containers on network can use to reach Overcast's HTTP API. Never returns empty: callers use the result for AWS_ENDPOINT_URL, and a wrong-but-plausible address degrades better than an unset one.

func ResolveHost

func ResolveHost(ctx context.Context, dc networkClient, network string, logger *zap.Logger) string

ResolveHost is Resolve without the scheme and port, for callers that pair the host with a port of their own — Lambda reaches its Runtime API on one port and the emulator API on another, but both live at the same host.

func ResourceHostnames

func ResourceHostnames(cfg *config.Config) []string

ResourceHostnames returns every base a *resource* hostname can be minted under: Hostnames plus "localhost".

The difference from Hostnames is the loopback name, and it turns on who the name is for. Hostnames answers "what can a container call Overcast", where a loopback name is a container's own address and therefore useless. A resource name — an RDS endpoint, an ElastiCache node — only ever has one of these as a *suffix* (`db.us-east-1.rds.localhost`), which is a perfectly good name for something else on the network, and it is the name Overcast mints when no OVERCAST_HOSTNAME is configured.

Services registering Docker network aliases need the whole set, because which name a caller holds depends on the endpoint that caller used. See docs/dev/container-networking.md.

func RunningInContainer

func RunningInContainer() bool

RunningInContainer reports whether this process is itself containerised (Docker's /.dockerenv sentinel) — exported for callers outside the resolver, e.g. cmd/overcast's startup guidance.

Types

type Listen

type Listen struct {
	// ContainerHost is the host containers use to reach the server. Never
	// empty: callers put it in an env var, and a wrong-but-plausible address
	// degrades better than an unset one.
	ContainerHost string

	// BindHosts are the local addresses to listen on, most important first —
	// the caller binds BindHosts[0] to settle a port of 0, so ContainerHost
	// leads whenever it is bindable here.
	BindHosts []string

	// Wildcard reports that BindHosts is the every-interface fallback rather
	// than a resolved set, so the caller can say so rather than looking narrow
	// when it is not.
	Wildcard bool
}

Listen is where a container-facing server should listen and what containers should be told to dial.

func ResolveListen

func ResolveListen(ctx context.Context, dc listenClient, network string, logger *zap.Logger) Listen

ResolveListen returns the address containers on network dial to reach a server on this host, and the local addresses that server must bind for it to work. Falls back to the wildcard rather than guessing: a server nobody can reach fails worse than one bound too widely, and Wildcard says which it is.

type Mapper

type Mapper struct {
	// contains filtered or unexported fields
}

Mapper adapts URLs and hostnames for one container network, given the endpoint containers on that network use to reach Overcast.

func New

func New(cfg *config.Config, endpoint string) *Mapper

New returns a Mapper for containers that reach Overcast at endpoint (an origin such as "http://172.18.0.1:4566" — see Resolve). A Mapper with an empty endpoint is inert rather than invalid: it leaves URLs alone and produces no /etc/hosts entries, so a caller that could not resolve an address still starts containers with the user's environment intact.

func (*Mapper) CABundleEnv

func (m *Mapper) CABundleEnv() map[string]string

CABundleEnv returns the environment variables that point a container's TLS stacks at the injected bundle, or nil when Overcast serves plain HTTP.

Coverage: AWS_CA_BUNDLE (botocore/CLI, Go SDK), NODE_EXTRA_CA_CERTS (Node — the Lambda runtimes Overcast ships), SSL_CERT_FILE (OpenSSL-based stacks, Ruby), REQUESTS_CA_BUNDLE (python-requests). The Java SDK reads only its own truststore and cannot be reached this way — documented in docs/https.md.

func (*Mapper) CABundleTar

func (m *Mapper) CABundleTar() ([]byte, error)

CABundleTar returns a tar archive (for CopyToContainer with destPath "/") carrying the PEM roots a container needs to verify Overcast's certificate: the local CA in auto mode, the operator's own certificate chain in explicit mode — the same split cmd/overcast uses for init-hook processes. (nil, nil) when TLS is off.

func (*Mapper) ClientEndpoint

func (m *Mapper) ClientEndpoint() string

ClientEndpoint returns the origin to hand container code as AWS_ENDPOINT_URL: the same server as Endpoint, but named rather than numbered.

A name is better than the address for two reasons. It survives Overcast's container being recreated on a different address, which an IP baked into a warm execution environment does not. And it can carry a subdomain, so an SDK that derives a virtual-hosted URL from the endpoint — S3's {bucket}.s3.{host}, API Gateway's {id}.execute-api.{region}.{host} — produces a name that resolves; from an IP endpoint the SDK is forced to path-style, and any URL it builds by prefixing labels is unusable.

Safe whether or not the resolver is running: the name is one of those ExtraHosts pins in /etc/hosts, so the endpoint itself always resolves, and only its subdomains depend on internal/dns. Falls back to the address when there is no usable name.

func (*Mapper) DNSServers

func (m *Mapper) DNSServers() []string

DNSServers returns the resolvers containers on this network should be given (Docker's HostConfig.Dns). Empty unless Overcast's DNS server is listening and Overcast has a container-routable address — pointing a container at a resolver that is not there would break all of its name resolution, which is far worse than the wildcard gap the resolver closes.

The address is Overcast's own on this network, the same target ExtraHosts uses; the server listens on every interface and answers each caller with the address reachable from its side.

func (*Mapper) Endpoint

func (m *Mapper) Endpoint() string

Endpoint returns the origin containers use to reach Overcast, suitable for AWS_ENDPOINT_URL. Empty when no address could be resolved.

func (*Mapper) ExtraHosts

func (m *Mapper) ExtraHosts() []string

ExtraHosts returns Docker --add-host entries ("name:target") that point every hostname Overcast might have minted a URL under at Overcast itself. Names that cannot usefully be shadowed — "localhost" (the container needs its own loopback) and bare IPs (already routable or already wrong) — are skipped.

func (*Mapper) RewriteBytes

func (m *Mapper) RewriteBytes(value []byte) []byte

RewriteBytes is RewriteURLs for a payload rather than a single value.

An invoke event carries the same host-minted URLs function environment does — a queue URL handed to Invoke by a host-side caller is the common one — and AWS SDKs resolve service endpoints from such a URL rather than from AWS_ENDPOINT_URL. Left alone, it sends the container's client to the container's own loopback. Real AWS never has this problem because its URLs are globally valid, so rewriting is what reproduces AWS's observable behaviour rather than diverging from it.

The input is returned unchanged when nothing matched, so an untouched payload is not copied.

func (*Mapper) RewriteURLs

func (m *Mapper) RewriteURLs(value string) string

RewriteURLs re-points Overcast URLs that a host-side caller minted — http://localhost:<port>/... and friends — at the endpoint the container can reach. Substring replacement rather than whole-value URL parsing, because deploy tools pass URLs inside JSON blobs and comma-separated lists as often as they pass them bare.

Only loopback origins on a port that is Overcast's own — the port it listens on, or the host port it is published on (see WithPublishedPort) — are rewritten. Anything else is left as the user set it.

func (*Mapper) WithPublishedPort

func (m *Mapper) WithPublishedPort(port int) *Mapper

WithPublishedPort records the host port Overcast's API is published on, so RewriteURLs also recognises URLs a host-side caller minted against it.

Needed because the two ports differ whenever Overcast's container remaps its API port (`docker run -p 4580:4566`): Overcast listens on 4566, but a host-side `cdk deploy` reaches it on 4580 and bakes queue URLs carrying that port into function and task environment. Matching only cfg.Port leaves those untouched and the container dials its own loopback on a dead port.

Pass 0 when the mapping is unknown — a native binary, or a container with no Docker socket to ask — and matching is unchanged. See PublishedPort.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL