Skip to main content
AI/MLjeremylongshore

flyio-security-basics

'Apply Fly.io security best practices for secrets management, private

Stars
2,267
Source
jeremylongshore/claude-code-plugins-plus-skills
Updated
2026-05-31
Slug
jeremylongshore--claude-code-plugins-plus-skills--flyio-security-basics
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/jeremylongshore/claude-code-plugins-plus-skills/HEAD/plugins/saas-packs/flyio-pack/skills/flyio-security-basics/SKILL.md -o .claude/skills/flyio-security-basics.md

Drops the SKILL.md into .claude/skills/flyio-security-basics.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Fly.io Security Basics

Overview

Fly.io deploys applications to edge locations worldwide using Firecracker microVMs. Security concerns center on deploy token scoping (org-wide vs per-app), secrets management (encrypted at rest, injected as env vars), private networking via WireGuard mesh (6PN), and TLS certificate management. A leaked deploy token can push arbitrary code to production machines across all regions.

API Key Management

function validateFlyToken(): void {
  const token = process.env.FLY_API_TOKEN;
  if (!token) {
    throw new Error("Missing FLY_API_TOKEN — use `fly tokens create deploy -a <app>`");
  }
  // Never log tokens; log only token type for debugging
  const isDeployToken = token.startsWith("FlyV1");
  console.log("Fly.io token loaded, type:", isDeployToken ? "deploy" : "personal");
}

Webhook Signature Verification

import crypto from "crypto";
import { Request, Response, NextFunction } from "express";

function verifyFlyWebhook(req: Request, res: Response, next: NextFunction): void {
  const signature = req.headers["x-fly-signature"] as string;
  const secret = process.env.FLY_WEBHOOK_SECRET!;
  const expected = crypto.createHmac("sha256", secret).update(req.body).digest("hex");
  if (!signature || !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    res.status(401).send("Invalid signature");
    return;
  }
  next();
}

Input Validation

import { z } from "zod";

const FlyDeploySchema = z.object({
  app_name: z.string().regex(/^[a-z0-9-]+$/).max(63),
  region: z.enum(["iad", "ord", "lax", "sjc", "ams", "lhr", "nrt", "syd", "gru"]),
  image: z.string().regex(/^registry\..+\/.+:.+$/),
  vm_size: z.enum(["shared-cpu-1x", "shared-cpu-2x", "performance-1x", "performance-2x"]).optional(),
  min_machines: z.number().int().min(0).max(20).optional(),
});

function validateDeployConfig(data: unknown) {
  return FlyDeploySchema.parse(data);
}

Data Protection

const FLY_SENSITIVE_FIELDS = ["fly_api_token", "deploy_token", "db_password", "wireguard_private_key", "tls_private_key"];

function redactFlyLog(record: Record<string, unknown>): Record<string, unknown> {
  const redacted = { ...record };
  for (const field of FLY_SENSITIVE_FIELDS) {
    if (field in redacted) redacted[field] = "[REDACTED]";
  }
  return redacted;
}

Security Checklist

  • All sensitive values in fly secrets, never in [env] section of fly.toml
  • Deploy tokens scoped per-app, not org-wide
  • force_https = true set in fly.toml [http_service]
  • Internal services use .internal DNS with no public ports
  • WireGuard keys rotated and unused tunnels removed
  • Secrets rotated on schedule (triggers rolling restart)
  • CI/CD uses deploy-scoped tokens, not personal tokens
  • Container images scanned before deployment

Error Handling

Vulnerability Risk Mitigation
Leaked deploy token Arbitrary code deployed to production Per-app scoped tokens + rotation
Secrets in fly.toml [env] Plaintext credentials in version control Use fly secrets set exclusively
Open internal ports Services exposed to public internet .internal DNS + NetworkPolicy
Org-wide token in CI All apps in org compromised via CI breach Deploy-scoped tokens per pipeline
Expired TLS certificates MITM attacks on custom domains Automated Let's Encrypt renewal

Resources

Next Steps

See flyio-prod-checklist.