Skip to content

Commit 6a30c5e

Browse files
committed
Changed default for secrets access permission
1 parent ae3d685 commit 6a30c5e

7 files changed

Lines changed: 44 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
2020
- Requests for unknown `Host` values no longer route to the default domain unless `system.fallback_unknown_domains` is explicitly enabled.
2121
- Delegated builds now require a valid bearer token on `/_openrun/delegate_build`. Builder nodes should run with `builder.mode = "delegate_server"` and no longer require `security.admin_over_tcp = true` for delegated-build ingress. Existing delegated-build setups must set the same `system.builder_auth_token` value on the main install and every builder node before upgrading.
2222
- CORS is disabled by default for apps. The default `app_config.cors.allow_origin` is now empty and `app_config.cors.allow_credentials` is now `"false"`. Apps that need browser cross-origin access must opt in with an app config override such as `cors.allow_origin="https://frontend.example.com"` or `cors.allow_origin="origin"`.
23+
- The default server-level `container.config(...)` permission no longer allows access to all secrets. Containerized apps that pass secrets through params, build args or generated secret volumes now need an explicitly approved `container.config` permission with the required `secrets=[...]` allowlist, unless the server config is intentionally changed to allow those secrets globally.
2324

2425
## [v0.16.26] - 2026-04-06
2526

docs/content/docs/Configuration/Secrets.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ hosted_domain = "example.com"
103103

104104
## Plugin Access to Secrets
105105

106-
For secrets which are passed to plugins, through app params or plugin arguments, the plugin needs to be authorized to access the secret. The permissions for each plugin are defined in the app definition. For example:
106+
For secrets which are passed to plugins, through app params or plugin arguments, the plugin needs to be authorized to access the secret. The default server permission for `container.config(...)` does not allow any secrets, so containerized apps that use secrets in params, build args need an explicitly approved `container.config` permission with a `secrets=[...]` allowlist.
107+
108+
The permissions for each plugin are defined in the app definition. For example:
107109

108110
```python {filename="app.star"}
109111
app = ace.app("test",
@@ -116,7 +118,15 @@ app = ace.app("test",
116118

117119
The secrets accessible are specified as a list of list of strings. In this case, the `{{secret "c1" "c2"}}` and `{{secret "TESTENV"}}` calls are allowed. Additional keys are also permitted.
118120

119-
If the key is specified as a string starting with `regex:`, then the subsequent part is a regex which is matched against the specified value. For example, `ace.permission("exec.in", "run", ["ls"], secrets=[["regex:TEST_.*"]),` allows accessing any secret starting with `TEST_`.
121+
If the key is specified as a string starting with `regex:`, then the subsequent part is a regex which is matched against the specified value. For example, `ace.permission("exec.in", "run", ["ls"], secrets=[["regex:TEST_.*"]])` allows accessing any secret starting with `TEST_`.
122+
123+
For a containerized app, the permission usually looks like:
124+
125+
```python {filename="app.star"}
126+
ace.permission("container.in", "config", [container.AUTO], secrets=[["DB_PASSWORD"]])
127+
```
128+
129+
The app must then be approved with that permission before `{{secret "DB_PASSWORD"}}` can be resolved for the container.
120130

121131
## Multiple Keys
122132

docs/content/docs/Configuration/Security.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ See [appsecurity]({{< ref "appsecurity" >}}) for details about the application l
7171

7272
## Default Plugin Permissions
7373

74-
OpenRun can allow plugin calls at the server level so apps do not need an explicit approved permission entry in app metadata. The default server permissions are:
74+
OpenRun can allow plugin calls at the server level so apps do not need an explicitly approved permission entry in app metadata. The default server permissions are:
7575

7676
```toml {filename="openrun.toml"}
7777
[[permissions.allow]]
@@ -83,17 +83,27 @@ arguments = ["<CONTAINER_URL>"]
8383
plugin = "container.in"
8484
method = "config"
8585
arguments = ["regex:.*"]
86-
secrets = [["regex:.*"]]
86+
secrets = [] # no secrets allowed by default
8787
```
8888

89-
`permissions.allow` adds globally approved plugin calls for all apps. `permissions.full_access` list grants the listed apps access to all plugin calls without requiring app-level approvals.
89+
`permissions.allow` adds globally approved plugin calls for all apps. If a permission entry includes `secrets`, that list controls which secrets the globally approved plugin call can resolve. An empty `secrets` list means the global approval does not grant access to any secret values. `permissions.full_access` list grants the listed apps access to all plugin calls without requiring app-level approvals.
9090

9191
The default OpenRun server config already includes two implicit approvals used by containerized apps:
9292

93-
- `proxy.in.config(container.URL, ...)`
94-
- `container.in.config(...)`
93+
- `proxy.config(container.URL, ...)`
94+
- `container.config(...)`
95+
96+
Because of these defaults, a standard containerized app does not need explicit `ace.permission(...)` entries just to call `proxy.config(container.URL)` and `container.config(...)`. The default `container.config(...)` approval does not allow secrets. If a containerized app passes secrets through params, build args or generated secret volumes, the app must declare and receive approval for a `container.config` permission with the required `secrets=[...]` allowlist, or the server config must be intentionally changed to allow those secrets globally.
97+
98+
For example, to restore blanket server-level secret access for containerized apps:
9599

96-
Because of these defaults, a standard containerized app does not need explicit `ace.permission(...)` entries just to call `proxy.config(container.URL)` and `container.config(...)`. If stricter control is needed, the app can still declare the permission explicitly, for example to narrow the allowed arguments or secrets.
100+
```toml {filename="openrun.toml"}
101+
[[permissions.allow]]
102+
plugin = "container.in"
103+
method = "config"
104+
arguments = ["regex:.*"]
105+
secrets = [["regex:.*"]]
106+
```
97107

98108
## CSRF Protection
99109

docs/content/docs/Develop.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@ param("preserve_host", type=BOOLEAN, description="Whether to preserve the origin
100100

101101
This is defining three parameters. The type can be one of `STRING`(default), `INT`, `BOOLEAN`, `LIST` and `DICT`. The param structure definition is
102102

103-
| Property | Optional | Type | Default | Notes |
104-
| :----------: | :------: | :----------------------------------------: | :---------------------: | :----------------------------------------------------------------------------------------------------------------: |
105-
| name | False | string | | Has to be a valid starlark keyword |
106-
| type | True | `STRING`, `INT`, `BOOLEAN`, `LIST`or`DICT` | `STRING` | The data type |
107-
| default | True | Type as set for `type` | Zero value for the type | |
108-
| description | True | string | | The description for the param |
109-
| required | True | bool | True | If required is True and default value is not specified, then validation fails |
103+
| Property | Optional | Type | Default | Notes |
104+
| :----------: | :------: | :----------------------------------------: | :---------------------: | :-----------------------------------------------------------------------------------------------------------------: |
105+
| name | False | string | | Has to be a valid starlark keyword |
106+
| type | True | `STRING`, `INT`, `BOOLEAN`, `LIST`or`DICT` | `STRING` | The data type |
107+
| default | True | Type as set for `type` | Zero value for the type | |
108+
| description | True | string | | The description for the param |
109+
| required | True | bool | True | If required is True and default value is not specified, then validation fails |
110110
| display_type | True | string | | How this param should be displayed in the UI. Options are `FILE`, `PASSWORD` and `TEXTAREA`, default is text input. |
111111

112112
The parameters are available in the app Starlark code, through the `param` namespace. For example, `param.port`, `param.app_name` etc. See https://github.com/openrundev/appspecs/blob/main/python-flask/app.star for an example of how this can be used.
@@ -171,7 +171,7 @@ app = ace.app("My App",
171171

172172
which completely specifies the app. This is saying that the app is using the container plugin to configure the container and the proxy plugin to proxy all API calls (`/` route) to the container URL. On the first API call to the app, OpenRun will build the image, start the container and proxy the API traffic to the appropriate port. No other configuration is required in Starlark. If the container spec does not define the port being exposed, then the container config needs to specify the port number to use. The port number can be parameterized.
173173

174-
With the default server config, `proxy.in.config(container.URL, ...)` and `container.in.config(...)` are already approved implicitly, so no explicit `ace.permission(...)` entries are required for this standard containerized app flow.
174+
With the default server config, `proxy.config(container.URL, ...)` and `container.config(...)` are already approved implicitly, so no explicit `ace.permission(...)` entries are required for this standard containerized app flow. That default approval does not allow `container.config(...)` to resolve secrets; apps that pass secrets to containers must request and receive approval for the required `secrets=[...]` allowlist.
175175

176176
[Containerized Apps]({{< ref "/docs/container" >}}) has more details on building containerized apps.
177177

@@ -189,7 +189,7 @@ For plugin calls made by the app, the plugin permissions normally have to be spe
189189

190190
For example `ace.permission("proxy.in", "config", [container.URL])` is a plugin call to `config` method in `proxy.in` plugin. The first argument has to be `container.URL`. Additional arguments are allowed. If no arguments are specified in the permission, then there is no restriction on arguments passed at runtime. If the value specified starts with `regex:`, then the value passed is checked against the specified regex at runtime.
191191

192-
The default server config already allows `proxy.in.config(container.URL, ...)` and `container.in.config(...)`, so these two calls do not need an explicit permission entry unless the app wants to narrow the default access.
192+
The default server config already allows `proxy.config(container.URL, ...)` and `container.config(...)`, so these two calls do not need an explicit permission entry unless the app wants to narrow the default access or allow specific secrets for `container.config(...)`.
193193

194194
See [secrets]({{< ref "/docs/configuration/secrets/#plugin-access-to-secrets" >}}) for details on specifying the secrets which can be accessed by the plugin call.
195195

docs/content/docs/Plugins/Container.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ app = ace.app("My App",
4848
)
4949
```
5050

51-
With the default server config, both `proxy.config(container.URL)` and `container.config(...)` are approved implicitly. Explicit app permissions are only needed here if you want to narrow the allowed arguments or secret access.
51+
With the default server config, both `proxy.config(container.URL)` and `container.config(...)` are approved implicitly. That default `container.config(...)` approval does not allow secrets. Add an explicit `ace.permission("container.in", "config", ..., secrets=[...])` entry when the app needs to pass secrets to the container, or when you want to narrow the allowed arguments.
5252

5353
An app which runs a command against a specified image (see [image-cmd spec](https://github.com/openrundev/appspecs/blob/main/image-cmd/app.star)) is
5454

@@ -71,6 +71,6 @@ app = ace.app(param.app_name + " : " + param.image ,
7171
)
7272
```
7373

74-
The explicit `container.in.run` permission is still required in this example. `container.in.config(...)` is implicitly approved by default. Add an explicit `ace.permission("container.in", "config", ...)` entry only if the app needs to restrict the arguments or secrets available to that config call.
74+
The explicit `container.in.run` permission is still required in this example. `container.config(...)` is implicitly approved by default without secret access. Add an explicit `ace.permission("container.in", "config", ..., secrets=[...])` entry if the config call needs to resolve secrets, or if the app needs to restrict the allowed arguments for that config call.
7575

7676
<!-- prettier-ignore-end -->

internal/system/config_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ func TestServerConfig(t *testing.T) {
8484
testutil.AssertEqualsInt(t, "proxy idle timeout", 15, c.AppConfig.Proxy.IdleConnTimeoutSecs)
8585
testutil.AssertEqualsBool(t, "proxy disable compression", true, c.AppConfig.Proxy.DisableCompression)
8686
testutil.AssertEqualsString(t, "secrets provider", "env", c.AppConfig.Security.DefaultSecretsProvider)
87+
testutil.AssertEqualsInt(t, "default permissions", 2, len(c.Permissions.Allow))
88+
testutil.AssertEqualsInt(t, "default container secrets", 0, len(c.Permissions.Allow[1].Secrets))
8789

8890
testutil.AssertEqualsString(t, "kubernetes namespace", "openrun", c.Kubernetes.Namespace)
8991
testutil.AssertEqualsString(t, "builder mode", "auto", c.Builder.Mode)

internal/system/openrun.default.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ arguments = ["<CONTAINER_URL>"]
112112
plugin = "container.in"
113113
method = "config"
114114
arguments = ["regex:.*"]
115-
secrets = [["regex:.*"]]
115+
secrets = [] # no secrets allowed by default, use [["regex:.*"]] to allow all secrets
116+
# to be accessed by apps which are using the container plugin
116117

117118
[app_config]
118119
# app config can be set at the app level using a metadata config update. For example:

0 commit comments

Comments
 (0)