Skip to content

Commit db0ebb7

Browse files
committed
feat: whitelist for allowed env and paths for lua
Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
1 parent f65dbc1 commit db0ebb7

12 files changed

Lines changed: 533 additions & 391 deletions

File tree

api/v1alpha1/envoyproxy_types.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,20 @@ type EnvoyProxySpec struct {
189189
// +optional
190190
LuaValidation *LuaValidation `json:"luaValidation,omitempty"`
191191

192+
// LuaValidationAllowlist defines the filesystem paths and environment variables that Lua
193+
// scripts from EnvoyExtensionPolicy resources are permitted to access during Strict
194+
// validation in the gateway controller.
195+
//
196+
// The allowlist is fail-closed: when unset or empty, Lua scripts are denied access to ALL
197+
// filesystem paths and environment variables during validation. Only entries that match the
198+
// allowlist are permitted.
199+
//
200+
// This field only takes effect when LuaValidation is Strict (the default). It has no effect
201+
// for the InsecureSyntax or Disabled validation modes, which do not execute the security sandbox.
202+
//
203+
// +optional
204+
LuaValidationAllowlist *LuaValidationAllowlist `json:"luaValidationAllowlist,omitempty"`
205+
192206
// DynamicModules defines the set of dynamic modules that are allowed to be
193207
// used by EnvoyExtensionPolicy resources and dynamic module load balancer
194208
// policies. Each entry registers a module by a logical name and specifies
@@ -250,6 +264,31 @@ const (
250264
LuaValidationDisabled LuaValidation = "Disabled"
251265
)
252266

267+
// LuaValidationAllowlist defines the filesystem paths and environment variables that Lua scripts
268+
// are permitted to access during Strict Lua validation in the gateway controller.
269+
// The allowlist is fail-closed: access to any path or environment variable that does not match an
270+
// entry below is denied.
271+
type LuaValidationAllowlist struct {
272+
// AllowedPaths is the list of filesystem path prefixes that Lua scripts are permitted to
273+
// access during validation (via io.open, io.input, io.output, io.lines, os.remove, os.rename).
274+
// A path is allowed when it equals an entry or is contained within an entry's subtree
275+
// (e.g. "/tmp" allows "/tmp/file.txt"). Paths are normalized (separators collapsed, made
276+
// absolute) before matching, and any "." or ".." traversal segment is always rejected.
277+
// When empty, all filesystem access is denied.
278+
//
279+
// +kubebuilder:validation:MaxItems=64
280+
// +optional
281+
AllowedPaths []string `json:"allowedPaths,omitempty"`
282+
283+
// AllowedEnvVars is the list of environment variable names that Lua scripts are permitted to
284+
// access during validation (via os.getenv, os.setenv). Matching is exact and case-sensitive.
285+
// When empty, access to all environment variables is denied.
286+
//
287+
// +kubebuilder:validation:MaxItems=64
288+
// +optional
289+
AllowedEnvVars []string `json:"allowedEnvVars,omitempty"`
290+
}
291+
253292
// RoutingType defines the type of routing of this Envoy proxy.
254293
type RoutingType string
255294

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,41 @@ spec:
776776
- InsecureSyntax
777777
- Disabled
778778
type: string
779+
luaValidationAllowlist:
780+
description: |-
781+
LuaValidationAllowlist defines the filesystem paths and environment variables that Lua
782+
scripts from EnvoyExtensionPolicy resources are permitted to access during Strict
783+
validation in the gateway controller.
784+
785+
The allowlist is fail-closed: when unset or empty, Lua scripts are denied access to ALL
786+
filesystem paths and environment variables during validation. Only entries that match the
787+
allowlist are permitted.
788+
789+
This field only takes effect when LuaValidation is Strict (the default). It has no effect
790+
for the InsecureSyntax or Disabled validation modes, which do not execute the security sandbox.
791+
properties:
792+
allowedEnvVars:
793+
description: |-
794+
AllowedEnvVars is the list of environment variable names that Lua scripts are permitted to
795+
access during validation (via os.getenv, os.setenv). Matching is exact and case-sensitive.
796+
When empty, access to all environment variables is denied.
797+
items:
798+
type: string
799+
maxItems: 64
800+
type: array
801+
allowedPaths:
802+
description: |-
803+
AllowedPaths is the list of filesystem path prefixes that Lua scripts are permitted to
804+
access during validation (via io.open, io.input, io.output, io.lines, os.remove, os.rename).
805+
A path is allowed when it equals an entry or is contained within an entry's subtree
806+
(e.g. "/tmp" allows "/tmp/file.txt"). Paths are normalized (separators collapsed, made
807+
absolute) before matching, and any "." or ".." traversal segment is always rejected.
808+
When empty, all filesystem access is denied.
809+
items:
810+
type: string
811+
maxItems: 64
812+
type: array
813+
type: object
779814
mergeGateways:
780815
description: |-
781816
MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure.

charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,41 @@ spec:
775775
- InsecureSyntax
776776
- Disabled
777777
type: string
778+
luaValidationAllowlist:
779+
description: |-
780+
LuaValidationAllowlist defines the filesystem paths and environment variables that Lua
781+
scripts from EnvoyExtensionPolicy resources are permitted to access during Strict
782+
validation in the gateway controller.
783+
784+
The allowlist is fail-closed: when unset or empty, Lua scripts are denied access to ALL
785+
filesystem paths and environment variables during validation. Only entries that match the
786+
allowlist are permitted.
787+
788+
This field only takes effect when LuaValidation is Strict (the default). It has no effect
789+
for the InsecureSyntax or Disabled validation modes, which do not execute the security sandbox.
790+
properties:
791+
allowedEnvVars:
792+
description: |-
793+
AllowedEnvVars is the list of environment variable names that Lua scripts are permitted to
794+
access during validation (via os.getenv, os.setenv). Matching is exact and case-sensitive.
795+
When empty, access to all environment variables is denied.
796+
items:
797+
type: string
798+
maxItems: 64
799+
type: array
800+
allowedPaths:
801+
description: |-
802+
AllowedPaths is the list of filesystem path prefixes that Lua scripts are permitted to
803+
access during validation (via io.open, io.input, io.output, io.lines, os.remove, os.rename).
804+
A path is allowed when it equals an entry or is contained within an entry's subtree
805+
(e.g. "/tmp" allows "/tmp/file.txt"). Paths are normalized (separators collapsed, made
806+
absolute) before matching, and any "." or ".." traversal segment is always rejected.
807+
When empty, all filesystem access is denied.
808+
items:
809+
type: string
810+
maxItems: 64
811+
type: array
812+
type: object
778813
mergeGateways:
779814
description: |-
780815
MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure.

internal/gatewayapi/luavalidator/lua_validator.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,59 @@ func (l *LuaValidator) getLuaValidation() egv1a1.LuaValidation {
9090
return egv1a1.LuaValidationStrict
9191
}
9292

93+
// allowlistData generates the Lua source consumed by security.lua: the path allowlist as a list and
94+
// the env var allowlist as a map (name -> true). The allowlist is fail-closed; an unconfigured
95+
// EnvoyProxy yields empty tables, denying all access.
96+
func (l *LuaValidator) allowlistData() string {
97+
var paths, envVars []string
98+
if l.envoyProxy != nil && l.envoyProxy.Spec.LuaValidationAllowlist != nil {
99+
paths = l.envoyProxy.Spec.LuaValidationAllowlist.AllowedPaths
100+
envVars = l.envoyProxy.Spec.LuaValidationAllowlist.AllowedEnvVars
101+
}
102+
103+
var b strings.Builder
104+
105+
b.WriteString("__lua_allowed_paths = {")
106+
for i, p := range paths {
107+
if i > 0 {
108+
b.WriteString(", ")
109+
}
110+
b.WriteString(luaStringLiteral(p))
111+
}
112+
b.WriteString("}\n")
113+
114+
b.WriteString("__lua_allowed_env_vars = {")
115+
for i, e := range envVars {
116+
if i > 0 {
117+
b.WriteString(", ")
118+
}
119+
// Spaces around the key are required: "[[[..." would lex as a long-string opener.
120+
b.WriteString("[ ")
121+
b.WriteString(luaStringLiteral(e))
122+
b.WriteString(" ] = true")
123+
}
124+
b.WriteString("}\n")
125+
126+
return b.String()
127+
}
128+
129+
// luaStringLiteral encodes s as a Lua long-bracket literal ([==[...]==]) with an equals level that
130+
// avoids any closing delimiter in s. Long brackets don't interpret escapes, so spec values are
131+
// passed verbatim as data and cannot inject Lua.
132+
func luaStringLiteral(s string) string {
133+
level := 0
134+
for strings.Contains(s, "]"+strings.Repeat("=", level)+"]") {
135+
level++
136+
}
137+
eq := strings.Repeat("=", level)
138+
// Lua strips a leading newline after the opening bracket; re-add it to preserve such values.
139+
prefix := ""
140+
if strings.HasPrefix(s, "\n") {
141+
prefix = "\n"
142+
}
143+
return "[" + eq + "[" + prefix + s + "]" + eq + "]"
144+
}
145+
93146
// newLuaState creates a new Lua state with global settings and resource limits applied
94147
// Returns the Lua state and a cancel function that must be called when done
95148
func (l *LuaValidator) newLuaState() (*lua.LState, context.CancelFunc) {
@@ -123,6 +176,8 @@ func (l *LuaValidator) runLua(code string) error {
123176

124177
// Execute mocks first (trusted code, needs setmetatable, defines StreamHandle, etc.)
125178
_ = L.DoString(mockData)
179+
// Inject the allowlists before security.lua, which reads and then clears them.
180+
_ = L.DoString(l.allowlistData())
126181
// Execute Lua security wrappers (trusted code) to protect the gateway controller
127182
// See security advisory: https://github.com/envoyproxy/gateway/security/advisories/GHSA-xrwg-mqj6-6m22
128183
_ = L.DoString(securityData)

0 commit comments

Comments
 (0)