From c21c3b0befeb46a51b6bf3758ffa30813bea0ff0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 9 Mar 2024 14:19:22 +0100 Subject: Adding upstream version 1.44.3. Signed-off-by: Daniel Baumann --- .../h2o/libh2o/srcdoc/configure/access_control.mt | 273 +++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 web/server/h2o/libh2o/srcdoc/configure/access_control.mt (limited to 'web/server/h2o/libh2o/srcdoc/configure/access_control.mt') diff --git a/web/server/h2o/libh2o/srcdoc/configure/access_control.mt b/web/server/h2o/libh2o/srcdoc/configure/access_control.mt new file mode 100644 index 000000000..4a613ac62 --- /dev/null +++ b/web/server/h2o/libh2o/srcdoc/configure/access_control.mt @@ -0,0 +1,273 @@ +? my $ctx = $main::context; +? $_mt->wrapper_file("wrapper.mt", "Configure", "Access Control")->(sub { + +

+Starting from version 2.1, H2O comes with a DSL-like mruby library which makes it easy to write access control list (ACL). +

+ +

Example

+ +

+Below example uses this Access Control feature to write various access control. +

+ +{example}->('Access Control', <<'EOT'); +paths: + "/": + mruby.handler: | + acl { + allow { addr == "127.0.0.1" } + deny { user_agent.match(/curl/i) && ! addr.start_with?("192.168.") } + respond(503, {}, ["Service Unavailable"]) { addr == malicious_ip } + redirect("https://example.com/", 301) { path =~ /moved/ } + use Htpasswd.new("/path/to/.htpasswd", "realm") { path.start_with?("/admin") } + } + file.dir: /path/to/doc_root +EOT +?> + +

+In the example, the handler you get by calling acl method will do the following: +

+ +

ACL Methods

+ +

+An ACL handler is built by calling ACL methods, which can be used like directives. +ACL methods can only be used in acl block. +

+ +

+Each ACL method adds a filter to the handler, which checks whether the request matches the provided condition or not. +Every ACL method can be accompanied by a condition block, which should return boolean value. +

+ +

+The filter defined by the method that first matched the accompanying condition gets applied (e.g. response 403 Forbidden, redirect to somewhere). +If a condition block is omitted, all requests matches. +If none of the conditions matches the request, the handler returns 399 and the request will be delegated to the next handler. +

+ +{mruby_method}->( + name => "allow", + desc => q{ Adds a filter which delegates the request to the next handler if the request matches the provided condition. }, +)->(sub { +?> +
allow { ..condition.. }
+? }) + +{mruby_method}->( + name => "deny", + desc => q{ Adds a filter which returns 403 Forbidden if the request matches the provided condition. }, +)->(sub { +?> +
deny { ..condition.. }
+? }) + +{mruby_method}->( + name => "redirect", + params => [ + { label => 'location', desc => 'Location to which the client will be redirected. Required.' }, + { label => 'status', desc => 'Status code of the response. Default value: 302' }, + ], + desc => q{ Adds a filter which redirects the client if the request matches the provided condition. }, +)->(sub { +?> +
redirect(location, status) { ..condition.. }
+? }) + +{mruby_method}->( + name => "respond", + params => [ + { label => 'status', desc => 'Status code of the response. Required.' }, + { label => 'header', desc => 'Header key-value pairs of the response. Default value: {}' }, + { label => 'body', desc => 'Body array of the response. Default value: []' }, + ], + desc => q{ Adds a filter which returns arbitrary response if the request matches the provided condition. }, +)->(sub { +?> +
respond(status, header, body) { ..condition.. }
+? }) + +{mruby_method}->( + name => "use", + params => [ + { label => 'proc', desc => 'Callable object that should be applied' }, + ], + desc => q{ Adds a filter which applies the provided handler (callable object) if the request matches the provided condition. }, +)->(sub { +?> +
use(proc) { ..condition.. }
+? }) + +

Matching Methods

+ +

+In a condition block, you can use helpful methods which return particular properties of the request as string values. +Matching methods can only be used in a condition block of the ACL methods. +

+ +{mruby_method}->( + name => "addr", + params => [ + { label => 'forwarded', desc => 'If true, returns the value of X-Forwarded-For header if it exists. Default value: true' }, + ], + desc => q{ Returns the remote IP address of the request. }, +)->(sub { +?> +
addr(forwarded)
+? }) + +{mruby_method}->( + name => "path", + desc => q{ Returns the requested path string of the request. }, +)->(sub { +?> +
path()
+? }) + +{mruby_method}->( + name => "method", + desc => q{ Returns the HTTP method of the request. }, +)->(sub { +?> +
method()
+? }) + +{mruby_method}->( + name => "header", + params => [ + { label => 'name', desc => 'Case-insensitive header name. Required.' }, + ], + desc => q{ Returns the header value of the request associated with the provided name. }, +)->(sub { +?> +
header(name)
+? }) + +{mruby_method}->( + name => "user_agent", + desc => q{ Shortcut for header("user-agent"). }, +)->(sub { +?> +
user_agent()
+? }) + +

Caution

+ +

+Several restrictions are introduced to avoid misconfiguration when using acl method. +

+If a configuration violates these restrictions, the server will detect it and refuse to launch with error message. +

+ +

+For example, both of the following examples violate the restrictions above, so the server will refuse to start up. +

+ +{example}->('Misconfiguration Example 1', <<'EOT'); +paths: + "/": + mruby.handler: | + acl { # this block will be ignored! + allow { addr == "127.0.0.1" } + } + acl { + deny + } + file.dir: /path/to/doc_root +EOT +?> + +{example}->('Misconfiguration Example 2', <<'EOT'); +paths: + "/": + mruby.handler: | + acl { # this block will be ignored! + allow { addr == "127.0.0.1" } + deny + } + proc {|env| [399, {}, []} + file.dir: /path/to/doc_root +EOT +?> + +

+You can correct these like the following: +

+ +{example}->('Valid Configuration Example', <<'EOT'); +paths: + "/": + mruby.handler: | + acl { + allow { addr == "127.0.0.1" } + deny + } + file.dir: /path/to/doc_root +EOT +?> + +

How-To

+ +

Matching IP Address Blocks

+ +

+You can match an IP address against predefined list of address blocks using a script named trie_addr.rb. +

+

+Below is an example. +

+ +{example}->('Address Block Matching Example', <<'EOT'); +paths: + "/": + mruby.handler: | + require "trie_addr.rb" + trie = TrieAddr.new.add(["192.168.0.0/16", "172.16.0.0/12"]) + acl { + allow { trie.match?(addr) } + deny + } + file.dir: /path/to/doc_root +EOT +?> + +

+This library currently supports only IPv4 addresses. TrieAddr#match? returns false when it receives an invalid IPv4 address (including an IPv6 address) as an argument.. +

+ +? }) -- cgit v1.2.3