Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
> **「CDN セキュリティを“設計思想ごと”再利用可能にし、
> 世界中の誰でも短時間で安全な初期構成を作れるようにする」**

**最初に推奨する導入ルート:** `npx cdn-security init --platform aws --archetype spa-static-site --force` から始め、生成された policy を build し、AWS CloudFront Function と WAF Terraform 出力を既存 IaC に組み込みます。Cloudflare Workers も対応していますが、現時点で最初の本番導入パスとして最も揃っているのは AWS + Terraform です。

---

## なぜこのフレームワークが必要か
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ The goal is simple.

> **"Make CDN security reusable as a design philosophy, so anyone in the world can build a secure initial setup in a short time."**

**Recommended first path:** start with `npx cdn-security init --platform aws --archetype spa-static-site --force`, build the generated policy, then wire the AWS CloudFront Function and WAF Terraform outputs into your existing infrastructure. Cloudflare Workers is also supported, but the AWS + Terraform path is the most complete first deployment path today.

---

## Why This Framework Is Needed
Expand Down
2 changes: 2 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ program
.option('--rule-group-only', 'AWS only: generate WAF rule groups without aws_wafv2_web_acl output')
.option('--fail-on-permissive', 'Exit non-zero when policy.metadata.risk_level is "permissive" (gate for production CI)')
.option('--fail-on-waf-approximation', 'Cloudflare only: exit non-zero when the policy relies on approximate or unsupported Cloudflare WAF mappings (see docs/cloudflare-waf-parity.md)')
.option('--allow-placeholder-token', 'Allow non-production placeholder credentials for static_token/basic_auth gates when referenced env vars are unset')
.action((opts) => {
const { compile } = require(path.join(pkgRoot, 'lib'));
const cwd = process.cwd();
Expand All @@ -213,6 +214,7 @@ program
ruleGroupOnly: !!opts.ruleGroupOnly,
failOnPermissive: !!opts.failOnPermissive,
failOnWafApproximation: !!opts.failOnWafApproximation,
allowPlaceholderToken: !!opts.allowPlaceholderToken,
cwd,
pkgRoot,
});
Expand Down
51 changes: 23 additions & 28 deletions docs/iac.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

## 概要

`npx cdn-security build`(および Infra 用に `npx cdn-security build --target aws`)のあと:
`npx cdn-security build` のあと:

| 出力 | 用途 |
|------|------|
| **dist/edge/viewer-request.js** | CloudFront Function (Viewer Request) |
| **dist/edge/viewer-response.js** | CloudFront Function (Viewer Response) |
| **dist/edge/cloudflare/index.ts** | Cloudflare Worker(`--target cloudflare` でビルドした場合)。出力は TypeScript。Wrangler がデプロイ時にコンパイルする。Wrangler を使わない場合は TypeScript ビルド環境が必要。 |
| **dist/infra/waf-rules.tf.json** | Terraform JSON: `aws_wafv2_rule_group`(レートベースルール)。ポリシーに `firewall.waf` がある場合に生成。 |
| **dist/infra/waf-rules.tf.json** | Terraform JSON: WAFv2 rule group / Web ACL resources。ポリシーに `firewall.waf` がある場合に生成。 |
| **dist/infra/waf-cloudformation.json** | AWS CloudFormation: `AWS::WAFv2::*` リソース。`emit-waf --format cloudformation` で生成。 |

---
Expand Down Expand Up @@ -72,43 +72,38 @@ resource "aws_cloudfront_distribution" "main" {

## Terraform: WAF(Infra)

ポリシーに **`firewall.waf`** セクションがあると、ビルドで **`dist/infra/waf-rules.tf.json`**(Terraform JSON)が出力されます。利用方法:
ポリシーに **`firewall.waf`** セクションがあると、ビルドで **`dist/infra/waf-rules.tf.json`**(Terraform JSON)が出力されます。最も単純な production path は、このファイルを CloudFront distribution を管理している Terraform root にコピーまたは生成し、生成された Web ACL ARN を distribution から参照する形です。

- **方法 A**: この JSON ファイルを Terraform のディレクトリに含め、`terraform plan` / `apply` でルールグループを管理する。
- **方法 B**: 生成されたルールグループを Web ACL から参照する。
### 推奨レイアウト

### 生成 waf-rules.tf.json の利用
```text
infra/
main.tf
cdn-security.auto.tf.json # dist/infra/waf-rules.tf.json からコピー
```

ファイルはそのまま Terraform JSON として有効です。
ビルドしてコピー:

1. **Terraform モジュールにコピー**: `waf-rules.tf.json` を Terraform のディレクトリに置き、そのディレクトリで `terraform plan` / `apply` を実行する。
2. **別モジュールから参照**: このルールグループを Web ACL の `rule_group_reference_statement` で参照する。
```bash
EDGE_ADMIN_TOKEN=replace-with-a-deploy-secret npx cdn-security build
cp dist/infra/waf-rules.tf.json infra/cdn-security.auto.tf.json
```

例: ビルド実行後、Terraform の設定ディレクトリで:
その後、生成された Web ACL を CloudFront distribution にアタッチします。生成リソース名には policy の `project` を sanitize した値が入ります。`project: example-cdn-security` の場合、Web ACL resource name は `aws_wafv2_web_acl.example_cdn_security` です。

```hcl
# 同一リポジトリ、または dist/infra/waf-rules.tf.json をこのディレクトリにコピー
# Web ACL でルールグループを参照:
resource "aws_wafv2_web_acl" "main" {
name = "main"
scope = "REGIONAL"
default_action { allow {} }
rule {
name = "rate-limit"
priority = 1
override_action { none {} }
statement {
rule_group_reference_statement {
arn = aws_wafv2_rule_group.example_cdn_security_rate_limit[0].arn
}
}
visibility_config { ... }
resource "aws_cloudfront_distribution" "main" {
# ...

web_acl_id = aws_wafv2_web_acl.example_cdn_security.arn

default_cache_behavior {
# ...
}
visibility_config { ... }
}
```

`dist/infra/` をそのまま使う場合は、リポジトリルートや `dist/infra/` を含むディレクトリで Terraform を実行すると、ルールグループが同じ state で定義されます
既存 Web ACL を別で管理している場合は、`npx cdn-security build --rule-group-only` を実行し、手書きの `aws_wafv2_web_acl` から生成 rule group を参照してください。生成 JSON は Web ACL と同じ Terraform state に置く必要があります。そうでないと Terraform から直接参照できません

---

Expand Down
51 changes: 23 additions & 28 deletions docs/iac.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ This document describes how to use the generated **`dist/edge/`** and **`dist/in

## Overview

After `npx cdn-security build` (and `npx cdn-security build --target aws` for Infra):
After `npx cdn-security build`:

| Output | Use |
|--------|-----|
| **dist/edge/viewer-request.js** | CloudFront Function (Viewer Request) |
| **dist/edge/viewer-response.js** | CloudFront Function (Viewer Response) |
| **dist/edge/cloudflare/index.ts** | Cloudflare Worker (when built with `--target cloudflare`). Output is TypeScript; Wrangler compiles it on deploy. Without Wrangler, a TypeScript build step is required. |
| **dist/infra/waf-rules.tf.json** | Terraform JSON: `aws_wafv2_rule_group` (rate-based rule). Use when policy has `firewall.waf`. |
| **dist/infra/waf-rules.tf.json** | Terraform JSON: WAFv2 rule group / Web ACL resources. Use when policy has `firewall.waf`. |
| **dist/infra/waf-cloudformation.json** | AWS CloudFormation: `AWS::WAFv2::*` resources. Generate with `emit-waf --format cloudformation`. |

---
Expand Down Expand Up @@ -72,43 +72,38 @@ If you generate `dist/edge/origin-request.js`, zip it and use `aws_lambda_functi

## Terraform: WAF (Infra)

When your policy includes a **`firewall.waf`** section, the build outputs **`dist/infra/waf-rules.tf.json`** (Terraform JSON). You can:
When your policy includes a **`firewall.waf`** section, the build outputs **`dist/infra/waf-rules.tf.json`** (Terraform JSON). The simplest production path is to copy or generate that file into the same Terraform root that owns your CloudFront distribution, then reference the generated Web ACL ARN from your distribution.

- **Option A**: Import the rule group by referencing the JSON file (e.g. `terraform plan` in a directory that includes this file, or use `terraform import` if the resource is created elsewhere).
- **Option B**: Use the generated rule group in your Terraform by **inlining** or **reading** the JSON and creating an `aws_wafv2_rule_group` resource that matches.
### Recommended layout

### Using the generated waf-rules.tf.json
```text
infra/
main.tf
cdn-security.auto.tf.json # copied from dist/infra/waf-rules.tf.json
```

The file is valid Terraform JSON. You can:
Build and copy:

1. **Copy into your Terraform module**: Place `waf-rules.tf.json` in a Terraform directory and run `terraform plan` / `apply` in that directory; Terraform will manage the rule group.
2. **Reference from another module**: Use a module that reads the JSON or use `terraform import` to attach the rule group to your Web ACL.
```bash
EDGE_ADMIN_TOKEN=replace-with-a-deploy-secret npx cdn-security build
cp dist/infra/waf-rules.tf.json infra/cdn-security.auto.tf.json
```

Example: ensure the build has run, then in a Terraform config directory:
Then attach the generated Web ACL. The generated resource names include the sanitized `project` value from your policy; if your policy uses `project: example-cdn-security`, the Web ACL resource name is `aws_wafv2_web_acl.example_cdn_security`.

```hcl
# In the same repo, or copy dist/infra/waf-rules.tf.json into this directory
# Then reference the rule group in your Web ACL:
resource "aws_wafv2_web_acl" "main" {
name = "main"
scope = "REGIONAL"
default_action { allow {} }
rule {
name = "rate-limit"
priority = 1
override_action { none {} }
statement {
rule_group_reference_statement {
arn = aws_wafv2_rule_group.example_cdn_security_rate_limit[0].arn
}
}
visibility_config { ... }
resource "aws_cloudfront_distribution" "main" {
# ...

web_acl_id = aws_wafv2_web_acl.example_cdn_security.arn
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correct generated Web ACL reference

For users following this Terraform example with project: example-cdn-security, this reference points to a resource that the generator never creates. The infra compiler keeps hyphens in the sanitized project name and appends -waf-acl for the Web ACL label (webAclName = projectName + '-waf-acl' in scripts/compile-infra.js), so copying waf-rules.tf.json will define a differently named aws_wafv2_web_acl and terraform plan will fail with an undeclared resource when this snippet is used.

Useful? React with 👍 / 👎.


default_cache_behavior {
# ...
}
visibility_config { ... }
}
```

If you keep the generated file in `dist/infra/`, run Terraform from the repo root or from a subdirectory that includes `dist/infra/waf-rules.tf.json` so the rule group is defined in the same state.
For existing Web ACL ownership, run `npx cdn-security build --rule-group-only` and attach the generated rule group from your hand-written `aws_wafv2_web_acl`. Keep the generated JSON in the same Terraform state as the Web ACL, otherwise Terraform cannot reference the generated resources directly.

---

Expand Down
12 changes: 10 additions & 2 deletions docs/quickstart.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ npm install --save-dev cdn-security-framework
npx cdn-security init
```

プラットフォーム(AWS CloudFront / Cloudflare Workers)とプロファイル(Strict / Balanced / Permissive)を選ぶと、`policy/security.yml` と `policy/profiles/<profile>.yml` が作成されます
プラットフォーム(AWS CloudFront / Cloudflare Workers)と、プロファイル(Strict / Balanced / Permissive)またはアーキタイプ(SPA / REST API / 管理画面 / マイクロサービス)を選ぶと、`policy/security.yml` と `policy/profiles/` または `policy/archetypes/` 配下の参照コピーが作成されます

非対話: `npx cdn-security init --platform aws --profile balanced --force`
最初の推奨ルート: `npx cdn-security init --platform aws --archetype spa-static-site --force`

## 2. ポリシー編集とビルド

`policy/security.yml` を必要に応じて編集し(allow_methods、block ルール、routes など)、次を実行します。

```bash
# policy に static_token 認証ゲートがある場合は、参照先の build-time secret を
# 先に設定します。組み込みの base/admin 例は EDGE_ADMIN_TOKEN を使います。
export EDGE_ADMIN_TOKEN=replace-with-a-deploy-secret

npx cdn-security build

# Cloudflare Workers
Expand All @@ -33,13 +38,16 @@ npx cdn-security build --target cloudflare
`/admin`、`/docs`、`/swagger` の保護用:

- 環境変数や CDN のシークレット管理(Terraform、Wrangler など)で `EDGE_ADMIN_TOKEN` を設定してください。
- ビルド時に変数が設定されていれば注入され、未設定の場合はプレースホルダーが入り、デプロイパイプラインで差し替え可能です
- ビルド時に変数が設定されていれば注入されます。local fixture build のみ、`npx cdn-security build --allow-placeholder-token` で明示的な insecure placeholder と警告を出せます。placeholder artifact は絶対にデプロイしないでください

`viewer-request.js` を手で編集する必要はありません。トークンはポリシー(routes.auth_gate.token_env)と環境変数で制御されます。

## 4. テスト

```bash
export EDGE_ADMIN_TOKEN=ci-build-token-not-for-deploy
export ORIGIN_SECRET=ci-origin-secret-not-for-deploy

npm run test:runtime
npm run test:unit
npm run test:drift
Expand Down
12 changes: 10 additions & 2 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ npm install --save-dev cdn-security-framework
npx cdn-security init
```

Choose platform (AWS CloudFront / Cloudflare Workers) and profile (Strict / Balanced / Permissive). This creates `policy/security.yml` and `policy/profiles/<profile>.yml`.
Choose platform (AWS CloudFront / Cloudflare Workers) and either a profile (Strict / Balanced / Permissive) or an archetype (SPA, REST API, admin, microservice). This creates `policy/security.yml` and a reference copy under `policy/profiles/` or `policy/archetypes/`.

Non-interactive: `npx cdn-security init --platform aws --profile balanced --force`
Recommended first path: `npx cdn-security init --platform aws --archetype spa-static-site --force`

## 2. Edit policy and build

Edit `policy/security.yml` as needed (allow_methods, block rules, routes, etc.), then:

```bash
# If your policy has a static_token auth gate, set the referenced build-time
# secret first. The built-in base/admin examples use EDGE_ADMIN_TOKEN.
export EDGE_ADMIN_TOKEN=replace-with-a-deploy-secret

# AWS (default)
npx cdn-security build

Expand All @@ -34,13 +39,16 @@ This validates the policy and generates **Edge Runtime** code into `dist/edge/`.
For `/admin`, `/docs`, `/swagger` protection:

- Set `EDGE_ADMIN_TOKEN` in your environment or CDN secret management (e.g. Terraform, Wrangler).
- The build injects it at compile time when the variable is set; otherwise it uses a placeholder you can replace in your deployment pipeline.
- The build injects it at compile time when the variable is set. For local fixture builds only, `npx cdn-security build --allow-placeholder-token` emits an explicit insecure placeholder and a warning. Never deploy placeholder artifacts.

You do **not** edit `viewer-request.js` by hand; the token is driven by policy (routes.auth_gate.token_env) and environment.

## 4. Test

```bash
export EDGE_ADMIN_TOKEN=ci-build-token-not-for-deploy
export ORIGIN_SECRET=ci-origin-secret-not-for-deploy

npm run test:runtime
npm run test:unit
npm run test:drift
Expand Down
1 change: 1 addition & 0 deletions emitter/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type CompileArtifactsOptions = {
target?: CompileTarget;
failOnPermissive?: boolean;
failOnWafApproximation?: boolean;
allowPlaceholderToken?: boolean;
outputMode?: string;
ruleGroupOnly?: boolean;
cwd?: string;
Expand Down
3 changes: 3 additions & 0 deletions emitter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,15 @@ function compileArtifacts(opts = {}) {
return { ok: false, errors, warnings, ...baseResult };
}
const permissiveFlag = opts.failOnPermissive ? ['--fail-on-permissive'] : [];
const placeholderFlag = opts.allowPlaceholderToken ? ['--allow-placeholder-token'] : [];
if (target === 'aws') {
const compilePath = path.join(pkgRoot, 'scripts', 'compile.js');
const compileResult = spawnSync(process.execPath, [
compilePath,
'--policy', policyPath,
'--out-dir', outDir,
...permissiveFlag,
...placeholderFlag,
], { cwd, encoding: 'utf8', env });
if (compileResult.status !== 0) {
collectFailedSpawn('edge compile', compileResult, errors);
Expand Down Expand Up @@ -120,6 +122,7 @@ function compileArtifacts(opts = {}) {
'--policy', policyPath,
'--out-dir', outDir,
...permissiveFlag,
...placeholderFlag,
], { cwd, encoding: 'utf8', env });
if (cfResult.status !== 0) {
collectFailedSpawn('cloudflare edge compile', cfResult, errors);
Expand Down
1 change: 1 addition & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface CompileOptions {
target?: CompileTarget;
failOnPermissive?: boolean;
failOnWafApproximation?: boolean;
allowPlaceholderToken?: boolean;
outputMode?: 'full' | 'rule-group' | string;
ruleGroupOnly?: boolean;
cwd?: string;
Expand Down
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@
"scripts/policy-lint.d.ts",
"scripts/lib/",
"templates/",
"examples/README.md",
"examples/README.ja.md",
"examples/aws-cloudfront/README.md",
"examples/aws-cloudfront/README.ja.md",
"examples/aws-cloudfront/package.json",
"examples/aws-cloudfront/package-lock.json",
"examples/aws-cloudfront/policy/",
"examples/cloudflare/README.md",
"examples/cloudflare/README.ja.md",
"examples/cloudflare/package.json",
"examples/cloudflare/package-lock.json",
"examples/cloudflare/policy/",
"policy/base.yml",
"policy/profiles/",
"policy/archetypes/",
Expand Down
Loading