From 37ca2a951a159216a9b74be7c8cb3c7ca8d335f9 Mon Sep 17 00:00:00 2001 From: Jonathan Hurter Date: Mon, 9 Nov 2026 15:50:27 +0100 Subject: [PATCH 1/2] feature(firewall_rules) Add the firewall rules API Related to STORY-4365 --- _scalingo_api/apps.md | 117 +++++++++++++++++++++++++++++++++ _scalingo_api/events.md | 76 +++++++++++++++++++++ _scalingo_json_schema/index.md | 26 ++++++++ scalingo.schema.json | 25 +++++++ 4 files changed, 244 insertions(+) diff --git a/_scalingo_api/apps.md b/_scalingo_api/apps.md index ed6ff68e..c777f94e 100644 --- a/_scalingo_api/apps.md +++ b/_scalingo_api/apps.md @@ -601,6 +601,123 @@ Returns 200 OK --- row --- +## List Application Firewall Rules + +`GET https://$SCALINGO_API_URL/v1/apps/[:app]/firewall_rules` + +Lists the app-level firewall rules for public HTTP(S) access. Each rule has an +`id`, an `app_id`, a `cidr`, and a `label`. The label is optional when creating +a rule. The `id` is required to retrieve or delete a rule. + +CIDRs must be IPv4 CIDR values only. IPv6 values and single IP addresses +without a prefix are invalid; use `/32` for one IPv4 address. + +||| col ||| + +Example request + +```sh +curl -H "Accept: application/json" -H "Content-Type: application/json" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -X GET "https://$SCALINGO_API_URL/v1/apps/example-app/firewall_rules" +``` + +Returns 200 OK + +```json +{ + "firewall_rules": [ + { + "id": "afw-82a3cac5-9b25-473e-b33d-6272b87e636f", + "app_id": "54100930736f7563d5030000", + "cidr": "203.0.113.42/32", + "label": "office" + } + ] +} +``` + +--- row --- + +## Get an Application Firewall Rule + +`GET https://$SCALINGO_API_URL/v1/apps/[:app]/firewall_rules/[:rule_id]` + +Retrieves a firewall rule by its ID. + +||| col ||| + +Example request + +```sh +curl -H "Accept: application/json" -H "Content-Type: application/json" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -X GET "https://$SCALINGO_API_URL/v1/apps/example-app/firewall_rules/afw-82a3cac5-9b25-473e-b33d-6272b87e636f" +``` + +Returns 200 OK + +```json +{ + "firewall_rule": { + "id": "afw-82a3cac5-9b25-473e-b33d-6272b87e636f", + "app_id": "54100930736f7563d5030000", + "cidr": "203.0.113.42/32", + "label": "office" + } +} +``` + +--- row --- + +## Create an Application Firewall Rule + +`POST https://$SCALINGO_API_URL/v1/apps/[:app]/firewall_rules` + +### Parameters + +* `firewall_rule.cidr`: IPv4 CIDR to allow +* `firewall_rule.label`: optional label attached to the rule + +||| col ||| + +Example request + +```sh +curl -H "Accept: application/json" -H "Content-Type: application/json" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -X POST "https://$SCALINGO_API_URL/v1/apps/example-app/firewall_rules" -d \ + '{ + "firewall_rule": { + "cidr": "203.0.113.42/32", + "label": "office" + } + }' +``` + +Returns 201 Created + +```json +{ + "firewall_rule": { + "id": "afw-82a3cac5-9b25-473e-b33d-6272b87e636f", + "app_id": "54100930736f7563d5030000", + "cidr": "203.0.113.42/32", + "label": "office" + } +} +``` + +--- row --- + +## Delete an Application Firewall Rule + +`DELETE https://$SCALINGO_API_URL/v1/apps/[:app]/firewall_rules/[:rule_id]` + +Returns 204 No Content. + +--- row --- + ## Update Application Settings `PATCH https://$SCALINGO_API_URL/v1/apps/[:app]` diff --git a/_scalingo_api/events.md b/_scalingo_api/events.md index 64f6052c..73b344f2 100644 --- a/_scalingo_api/events.md +++ b/_scalingo_api/events.md @@ -86,6 +86,82 @@ Example object: --- row --- +* **New app firewall rule event** + +_When:_ When an Application Firewall rule is added +`type=new_app_firewall_rule` + +{:.table} +| field | type | description | +| ------- | ------ | ----------------------------------- | +| rule_id | string | Unique ID of the firewall rule | +| cidr | string | CIDR range allowed by the rule | +| label | string | Optional label attached to the rule | + +||| col ||| + +Example object: + +```json +{ + "id": "54dcdd4a73636100011a0000", + "created_at": "2015-02-12T18:05:14.226+01:00", + "user": { + "username": "johndoe", + "email": "john@doe.com", + "id": "us-0e6d8e46-5cd0-42a4-acba-372b2be605ac" + }, + "app_id": "5343eccd646173000a140000", + "app_name": "appname", + "type": "new_app_firewall_rule", + "type_data": { + "rule_id": "afw-82a3cac5-9b25-473e-b33d-6272b87e636f", + "cidr": "203.0.113.42/32", + "label": "Office" + } +} +``` + +--- row --- + +* **Delete app firewall rule event** + +_When:_ When an Application Firewall rule is deleted +`type=delete_app_firewall_rule` + +{:.table} +| field | type | description | +| ------- | ------ | ----------------------------------- | +| rule_id | string | Unique ID of the firewall rule | +| cidr | string | CIDR range allowed by the rule | +| label | string | Optional label attached to the rule | + +||| col ||| + +Example object: + +```json +{ + "id": "54dcdd4a73636100011a0000", + "created_at": "2015-02-12T18:05:14.226+01:00", + "user": { + "username": "johndoe", + "email": "john@doe.com", + "id": "us-0e6d8e46-5cd0-42a4-acba-372b2be605ac" + }, + "app_id": "5343eccd646173000a140000", + "app_name": "appname", + "type": "delete_app_firewall_rule", + "type_data": { + "rule_id": "afw-82a3cac5-9b25-473e-b33d-6272b87e636f", + "cidr": "203.0.113.42/32", + "label": "Office" + } +} +``` + +--- row --- + * **Rename app event** _When:_ When the application is renamed to a new name diff --git a/_scalingo_json_schema/index.md b/_scalingo_json_schema/index.md index 44a7e253..7e7eba08 100644 --- a/_scalingo_json_schema/index.md +++ b/_scalingo_json_schema/index.md @@ -40,6 +40,7 @@ You can use [the provided scalingo.schema.json](https://github.com/Scalingo/deve | website | string | Official website of the application if any | | copy_parent_database_urls | boolean | Copy database credentials from parent app | | env | object | Environment of the application, see below | +| firewall_rules | array | Application firewall rules for review apps, see below | | addons | array | List of all the addons required to run the app | | scripts | object | Optional hook scripts, see below | | formation | object | Formation of containers when an app is created | @@ -128,6 +129,12 @@ scalingo.json example } } ], + "firewall_rules": [ + { + "cidr": "203.0.113.42/32", + "label": "office" + } + ], "scripts": { "first-deploy": "echo 'first deployment'", "postdeploy": "echo hello" @@ -147,6 +154,25 @@ scalingo.json example --- row --- +**Application Firewall** + +Review apps inherit app firewall rules from their parent app when the +`firewall_rules` key is omitted. Set this key to an array of rules to replace +the inherited rules for the child app. An empty array (`"firewall_rules": []`) +disables inheritance and creates the review app without firewall rules. + +Each rule must have a `cidr` and can have an optional `label`. + +{:.table} +| field | type | description | +| ----- | ------ | ------------------------------------------------------ | +| cidr | string | IPv4 CIDR to allow. Use `/32` for a single IPv4 address | +| label | string | Optional label attached to the rule | + +IPv6 CIDRs and single IP addresses without a prefix are invalid. + +--- row --- + **Addons** If no `addons` key is specified, the default behaviour is to duplicate the diff --git a/scalingo.schema.json b/scalingo.schema.json index 8623653c..1276ec68 100644 --- a/scalingo.schema.json +++ b/scalingo.schema.json @@ -91,10 +91,35 @@ "plan": "postgresql:postgresql-sandbox" } ] + }, + "firewall_rules": { + "$ref": "#/definitions/firewall_rules" } }, "required": ["name"], "definitions": { + "firewall_rules": { + "description": "Override the parent app firewall rules for a review app. Omit this key to inherit the parent rules, or use an empty array to create the review app without firewall rules. CIDRs must be IPv4 CIDR values. Use /32 for a single IPv4 address.", + "type": "array", + "examples": [[{"cidr": "203.0.113.42/32", "label": "office"}]], + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "cidr": { + "description": "IPv4 CIDR to allow", + "type": "string", + "pattern": "^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\\/(3[0-2]|[12]?[0-9])$", + "examples": ["203.0.113.42/32", "10.0.0.0/24"] + }, + "label": { + "description": "Optional label attached to the rule", + "type": "string" + } + }, + "required": ["cidr"] + } + }, "addons": { "description": "If no addons key is specified, the default behaviour is to duplicate the addons from the parent application.\n\nThe addons field contains an array of object describing the addons you need to deploy for your review app.\n\nSee https://doc.scalingo.com/addons", From b3a205661f1275ab27d2291f03489131ef5affe5 Mon Sep 17 00:00:00 2001 From: Jonathan Hurter Date: Mon, 9 Nov 2026 18:11:27 +0100 Subject: [PATCH 2/2] feature(firewall_rules) Fix formatting and wording --- _scalingo_api/apps.md | 2 +- _scalingo_json_schema/index.md | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/_scalingo_api/apps.md b/_scalingo_api/apps.md index c777f94e..2422a2b8 100644 --- a/_scalingo_api/apps.md +++ b/_scalingo_api/apps.md @@ -609,7 +609,7 @@ Lists the app-level firewall rules for public HTTP(S) access. Each rule has an `id`, an `app_id`, a `cidr`, and a `label`. The label is optional when creating a rule. The `id` is required to retrieve or delete a rule. -CIDRs must be IPv4 CIDR values only. IPv6 values and single IP addresses +CIDRs must be IPv4 CIDR values only. IPv6 values and single IPv4 addresses without a prefix are invalid; use `/32` for one IPv4 address. ||| col ||| diff --git a/_scalingo_json_schema/index.md b/_scalingo_json_schema/index.md index 7e7eba08..91d3edc5 100644 --- a/_scalingo_json_schema/index.md +++ b/_scalingo_json_schema/index.md @@ -29,21 +29,21 @@ You can use [the provided scalingo.schema.json](https://github.com/Scalingo/deve **The manifest attributes** {:.table} -| field | type | description | -| ------------------------- | ------ | ---------------------------------------------- | -| name | string | Complete name of the project | -| repository | string | Location of the GIT repository of the project | -| ref | string | Branch name or commit ID of the code to use | -| stack | string | Name of the stack to use | -| description | string | Description in one or two sentences of the app | -| logo | string | URL to the logo of the project | -| website | string | Official website of the application if any | -| copy_parent_database_urls | boolean | Copy database credentials from parent app | -| env | object | Environment of the application, see below | +| field | type | description | +| ------------------------- | ------- | ----------------------------------------------------- | +| name | string | Complete name of the project | +| repository | string | Location of the GIT repository of the project | +| ref | string | Branch name or commit ID of the code to use | +| stack | string | Name of the stack to use | +| description | string | Description in one or two sentences of the app | +| logo | string | URL to the logo of the project | +| website | string | Official website of the application if any | +| copy_parent_database_urls | boolean | Copy database credentials from parent app | +| env | object | Environment of the application, see below | | firewall_rules | array | Application firewall rules for review apps, see below | -| addons | array | List of all the addons required to run the app | -| scripts | object | Optional hook scripts, see below | -| formation | object | Formation of containers when an app is created | +| addons | array | List of all the addons required to run the app | +| scripts | object | Optional hook scripts, see below | +| formation | object | Formation of containers when an app is created | **Environment** @@ -169,7 +169,7 @@ Each rule must have a `cidr` and can have an optional `label`. | cidr | string | IPv4 CIDR to allow. Use `/32` for a single IPv4 address | | label | string | Optional label attached to the rule | -IPv6 CIDRs and single IP addresses without a prefix are invalid. +IPv6 CIDRs and single IPv4 addresses without a prefix are invalid. --- row ---