├── .bazelrc ├── .circleci └── config.yml ├── .dockerignore ├── .gitattributes ├── .gitignore ├── .gitmodules ├── BUILD ├── LICENSE ├── README.md ├── WORKSPACE ├── ci ├── Dockerfile-envoy-image ├── README.md ├── WORKSPACE.filter.example ├── build_container │ ├── Dockerfile-centos │ ├── Dockerfile-ubuntu │ ├── build_container_modsecurity_ubuntu.sh │ └── docker_build.sh ├── do_ci.sh ├── do_envoy_ci.sh ├── docker-entrypoint.sh ├── docker_build.sh └── run_envoy_docker.sh ├── conf ├── envoy-modsecurity-example-lds.yaml ├── envoy-modsecurity-example.yaml ├── lds.yaml ├── modsecurity.conf ├── modsecurity.v3.0.3.conf └── unicode.mapping ├── http-filter-modsecurity ├── BUILD ├── README.md ├── http_filter.cc ├── http_filter.h ├── http_filter.proto ├── http_filter_config.cc ├── http_filter_integration_test.cc ├── utility.cc ├── utility.h ├── webhook_fetcher.cc ├── webhook_fetcher.h └── well_known_names.h └── modsecurity ├── include └── libmodsecurity.a /.bazelrc: -------------------------------------------------------------------------------- 1 | import %workspace%/envoy/.bazelrc 2 | build --workspace_status_command=envoy/bazel/get_workspace_status 3 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | references: 2 | envoy-build-image: &envoy-build-image 3 | envoyproxy/envoy-build:latest 4 | 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | - image: *envoy-build-image 10 | resource_class: xlarge 11 | steps: 12 | - checkout 13 | - run: git submodule update --init 14 | - run: ./ci/do_ci.sh build 15 | test: 16 | docker: 17 | - image: *envoy-build-image 18 | resource_class: xlarge 19 | steps: 20 | - checkout 21 | - run: git submodule update --init 22 | - run: ./ci/do_ci.sh test 23 | 24 | workflows: 25 | version: 2 26 | all: 27 | jobs: 28 | - build 29 | - test 30 | 31 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .cache 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | http-filter-modsecurity/libmodsecurity.a filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-* 2 | .idea 3 | owasp-modsecurity* 4 | .project 5 | .vscode 6 | .cache 7 | build_release*/ 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "envoy"] 2 | path = envoy 3 | url = https://github.com/envoyproxy/envoy.git 4 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | load( 4 | "@envoy//bazel:envoy_build_system.bzl", 5 | "envoy_cc_binary", 6 | "envoy_cc_library", 7 | "envoy_cc_test" 8 | ) 9 | 10 | alias( 11 | name = "envoy", 12 | actual = ":envoy-static", 13 | ) 14 | 15 | envoy_cc_binary( 16 | name = "envoy-static", 17 | repository = "@envoy", 18 | deps = [ 19 | ":libmodsecurity", 20 | "//http-filter-modsecurity:http_filter_lib", 21 | "//http-filter-modsecurity:http_filter_config", 22 | "@envoy//source/exe:envoy_main_entry_lib", 23 | ], 24 | # Note - this really adds those as dynamic dependencies, this forces our docker image to have these libraries installed 25 | linkopts = ["-lyajl", "-ldl", "-lrt", "-lpcre", "-lcurl", "-lxml2", "-lGeoIP"] 26 | ) 27 | 28 | cc_import( 29 | name = "libmodsecurity", 30 | hdrs = glob(["modsecurity/include/**"]), 31 | static_library = "modsecurity/libmodsecurity.a", 32 | visibility = ["//visibility:public"] 33 | ) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 octarinesec 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModSecurity-envoy 2 | The ModSecurity-Envoy is Envoy version compiled with HTTP filter (can be opt-in/out) running ModSecurity (V3). 3 | In other words you can run and configure WAF (ModSecurity) rules on HTTP Traffic that flows through envoy. 4 | 5 | The most common use case is for ModSecurity-Envoy is to apply WAF on East-West traffic inside kubernetes deployments. 6 | As Envoy is the de-facto standard proxy in kubernetes deployments and is usually deployed in every pod you can deploy 7 | this Envoy version and Enable ModSecurity-Envoy Filter on all pods or on the most important ones. 8 | 9 | Some of the ideas behind the project are described in this [blog](https://github.com/octarinesec/ModSecurity-envoy) 10 | 11 | ## Compilation 12 | 13 | ### Dependencies 14 | 15 | ModSecurity-Envoy depends on Envoy (as a git submodule) and ModSecurity (a sibling directory). 16 | The [modsecurity](./modsecurity) directory contains two symbolic links to the sibling directory. 17 | 18 | ```bash 19 | git clone git@github.com:octarinesec/ModSecurity-envoy.git 20 | git clone git@github.com:SpiderLabs/ModSecurity.git 21 | 22 | cd ModSecurity-envoy 23 | git submodule update --init 24 | ``` 25 | 26 | The directory structure should be as follows: 27 | ``` 28 | . 29 | +-- ModSecurity-envoy 30 | | +-- modsecurity 31 | | | +-- include -> ../../ModSecurity/headers 32 | | | +-- libmodsecurity.a -> ../../ModSecurity/src/.libs/libmodsecurity.a 33 | +-- ModSecurity 34 | ``` 35 | 36 | For more details on how to compile ModSecurity read ModSecurity's [documentation](https://github.com/SpiderLabs/ModSecurity#compilation). 37 | 38 | ### Compiling on host 39 | 40 | You can compile ModSecurity-Envoy on host, the same as you would compile Envoy. 41 | However, you will need these additional dependencies: 42 | 43 | ```bash 44 | sudo apt-get install -y libtool cmake realpath clang-format-5.0 automake 45 | sudo apt-get install -y g++ flex bison curl doxygen libyajl-dev libgeoip-dev libtool dh-autoreconf libcurl4-gnutls-dev libxml2 libpcre++-dev libxml2-dev 46 | ``` 47 | 48 | To build run 49 | ```bash 50 | bazel build //:envoy 51 | ``` 52 | 53 | For more information on envoy's building system read Envoy's [documentation](https://github.com/envoyproxy/envoy). 54 | 55 | ### Using the docker images 56 | 57 | You can build docker images for envoy-build and envoy 58 | See [ci/README.md](ci/README.md) 59 | 60 | ## Configuration 61 | 62 | ModSecurity-Envoy Filter accept the configuration defined in [http_filter.proto](./http-filter-modsecurity/http_filter.proto) 63 | 64 | You will need to modify the Envoy config file to add the filter to the filter chain for a particular HTTP route configuration. 65 | See the examples in [conf](conf). 66 | 67 | Note: By adding metadata to specific routes, you can have granular control to disable the filter: 68 | ```yaml 69 | metadata: 70 | filter_metadata: 71 | envoy.filters.http.modsecurity: 72 | # To only disable requests / responses processing 73 | # disable_request: true 74 | # disable_response: true 75 | # Or, as a shorthand, use disable to disable both 76 | disable: true 77 | ``` 78 | 79 | The configuration for the filter is provided under the http_filters: 80 | ```yaml 81 | http_filters: 82 | # before envoy.router because order matters! 83 | - name: envoy.filters.http.modsecurity 84 | config: 85 | # ModSecurity rules can either be provided by a path 86 | rules_path: /etc/modsecurity.conf 87 | # Additionally you can provide inline rules (will be loaded after processing the rules_path, if provided) 88 | rules_inline: | 89 | # ModSecurity rules 90 | # ... 91 | # Optionally, you can provide a webhook configuration 92 | webhook: 93 | # The http_uri field is mandatory 94 | http_uri: 95 | uri: http://localhost:10000/wh_callback 96 | cluster: service2 97 | timeout: 98 | seconds: 3 99 | # Optionally you can provide a secret to sign the webhooks with an HMAC-256 (for more information see the .proto file) 100 | secret: webhook_secret 101 | - name: envoy.router 102 | config: {} 103 | ``` 104 | 105 | ## OWASP ModSecurity Core Rule Set (CRS) 106 | 107 | CRS is a set of generic attack 108 | detection rules for use with ModSecurity and aims to protect web applications 109 | from wide range of attacks. For more information check out [https://modsecurity.org/crs/](https://modsecurity.org/crs/) 110 | 111 | Download and extract the latest rules to the directory. 112 | 113 | ```bash 114 | wget https://github.com/SpiderLabs/owasp-modsecurity-crs/archive/v3.1.1.tar.gz 115 | tar xvzf v3.1.1.tar.gz 116 | ``` 117 | 118 | The configuration examples include the relevant OWASP rules. 119 | See [./conf/modsecurity.conf](./conf/modsecuirty.conf) and [./conf/lds.yaml](./conf/lds.yaml) for usage example. 120 | 121 | ## Testing 122 | 123 | TODO 124 | 125 | ## How it works 126 | 127 | First let's run an echo server that we will use as our upstream 128 | 129 | ```bash 130 | docker run -p 5555:80 kennethreitz/httpbin 131 | ``` 132 | 133 | Now let's run the envoy 134 | 135 | ```bash 136 | sudo ./bazel-bin/envoy-static -c conf/envoy-modsecurity-example-lds.yaml -l info 137 | ``` 138 | 139 | Make our first request 140 | ```bash 141 | curl -X GET "http://127.0.0.1:8585/get" -H "accept: application/json" 142 | ``` 143 | 144 | Let's download Nikto which is the most popular Open Source web server scanner 145 | 146 | ```bash 147 | wget https://github.com/sullo/nikto/archive/master.zip 148 | unzip master.zip 149 | perl nikto-master/program/nikto.pl -h localhost:5555 150 | ``` 151 | 152 | Now we can `cat /var/log/modsec_audit.log` and see all detected attacks which in production 153 | can be piped to a SIEM of your choice or any other centralized log. 154 | 155 | Let's try and add our own RULE as each WAF are designed to be configurable to protect 156 | different web applications. 157 | 158 | Make sure the following line is in `modsecurity-example.conf` or in the configuration under rules_inline. 159 | 160 | `SecRule ARGS:param1 "test" "id:1,deny,msg:'this',msg:'is',msg:'a',msg:'test'"` 161 | 162 | This line will detect any url with argument ?param1=test param. 163 | 164 | reload Envoy's configuration and execute the following command 165 | `curl -X GET "http://127.0.0.1:8585/get?param1=test" -H "accept: application/json"` 166 | 167 | check the logs via `tail -f` and you will see the following output 168 | 169 | ```bash 170 | ModSecurity: Warning. Matched "Operator `Rx' with parameter `test' against variable `ARGS:param1' (Value: `test' ) [file "crs-setup.conf"] [line "7"] [id "1"] [rev ""] [msg "test"] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname ""] [uri "/"] [unique_id "152991475598.002681"] [ref "o0,4v13,4"] 171 | ``` 172 | 173 | If we have a webhook installed, triggering a rule will result in a similar request: 174 | 175 | ``` 176 | POST /wh_callback 177 | Headers: 178 | host: localhost:10000 179 | content-type: application/json 180 | content-length: 530 181 | x-envoy-webhook-signature-type: HMAC-SHA256 182 | x-envoy-webhook-signature-value: d7c224c82cee677e32dc3ae0d2e60fae5a0c9714613b55bd0791fd226d8f22e7 183 | x-envoy-internal: true 184 | x-envoy-expected-rq-timeout-ms: 3000 185 | 186 | {"accuracy": 0, "clientIpAddress": "127.0.0.1", "data": "", "id": "156690498720.063187", "isDisruptive": false, "match": "Matched \"Operator `Rx' with parameter `test' against variable `ARGS:param1' (Value: `test' )", "maturity": 0, "message": "Test rule", "noAuditLog": false, "phase": 1, "reference": "o0,4v13,4", "rev": "", "ruleFile": "<>", "ruleId": 1, "ruleLine": 6, "saveMessage": true, "serverIpAddress": "127.0.0.1", "severity": 0, "uriNoQueryStringDecoded": "/", "ver": "", "tags": []} 187 | ``` -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # The workspace itself is based on envoy-filter-example 2 | # See project's README for more documentation 3 | 4 | workspace(name = "envoy_filter_modsecurity") 5 | 6 | local_repository( 7 | name = "envoy", 8 | path = "envoy", 9 | ) 10 | 11 | # This is directly copied from envoy/WORKSPACE (with the added @envoy prefix) 12 | # In case things break, you may want to copy-paste again. 13 | # TODO - can we avoid this by loading envoy's workspace? 14 | 15 | load("@envoy//bazel:api_repositories.bzl", "envoy_api_dependencies") 16 | 17 | envoy_api_dependencies() 18 | 19 | load("@envoy//bazel:repositories.bzl", "GO_VERSION", "envoy_dependencies") 20 | load("@envoy//bazel:cc_configure.bzl", "cc_configure") 21 | 22 | envoy_dependencies() 23 | 24 | load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies") 25 | 26 | rules_foreign_cc_dependencies() 27 | 28 | cc_configure() 29 | 30 | load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") 31 | 32 | go_rules_dependencies() 33 | 34 | go_register_toolchains(go_version = GO_VERSION) 35 | -------------------------------------------------------------------------------- /ci/Dockerfile-envoy-image: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y software-properties-common 5 | 6 | RUN add-apt-repository ppa:ubuntu-toolchain-r/test -y \ 7 | && apt-get update \ 8 | && apt-get upgrade -y \ 9 | && apt-get install -y ca-certificates \ 10 | # Add these to support dynamic requirements for envoy 11 | && apt-get install -y libstdc++6 libyajl2 libgeoip1 libpcre++0v5 libxml2 curl libcurl4-gnutls-dev \ 12 | && apt-get autoremove -y \ 13 | && apt-get clean \ 14 | && rm -rf /tmp/* /var/tmp/* \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | RUN mkdir -p /etc/envoy 18 | 19 | ADD build_release_stripped/envoy /usr/local/bin/envoy 20 | ADD ./envoy/configs/google_com_proxy.v2.yaml /etc/envoy/envoy.yaml 21 | 22 | COPY v3.1.1.tar.gz /etc/envoy/ 23 | RUN cd /etc/envoy && tar -xzf v3.1.1.tar.gz && rm v3.1.1.tar.gz 24 | 25 | COPY conf /etc/envoy 26 | # Replace relative path to aboslute 27 | RUN sed -i 's#conf/modsecurity.conf#/etc/envoy/modsecurity.conf#' /etc/envoy/envoy-modsecurity-example.yaml 28 | 29 | EXPOSE 10000 30 | 31 | COPY ci/docker-entrypoint.sh / 32 | ENTRYPOINT ["/docker-entrypoint.sh"] 33 | CMD ["envoy", "-c", "/etc/envoy/envoy-modsecurity-example.yaml"] 34 | -------------------------------------------------------------------------------- /ci/README.md: -------------------------------------------------------------------------------- 1 | # Envoy CI 2 | For more details see [envoy/ci/README.md](https://github.com/envoyproxy/envoy/blob/master/ci/README.md) 3 | 4 | # Build image for Modsecurity-envoy 5 | 6 | The build image for ModSecurity is different than the regular envoy's build image. 7 | (because of dependencies on additional libraries, see ./build_container/build_container_modsecurity_ubuntu.sh) 8 | Thus we need to rebuild the image. 9 | To build a local clone of the build image you can make changes to files such as 10 | build_container.sh locally and then run: 11 | 12 | ```bash 13 | DISTRO=ubuntu 14 | cd ci/build_container 15 | LINUX_DISTRO="${DISTRO}" CIRCLE_SHA1=modsecurity_v1 ./docker_build.sh # Wait patiently for quite some time 16 | ``` 17 | 18 | This builds the Ubuntu based `envoyproxy/envoy-build-ubuntu` image 19 | 20 | # Building and running tests as a developer 21 | 22 | You can either use envoy-filter-example's ./ci/do_ci.sh to create a run simple build and tests. 23 | 24 | ```bash 25 | sudo IMAGE_NAME=envoyproxy/envoy-build-ubuntu IMAGE_ID=modsecurity_v1 ./ci/run_envoy_docker.sh './ci/do_ci.sh build' 26 | ``` 27 | 28 | Or you can use do_envoy_ci.sh which acts as a proxy for envoy's do_ci.sh (for more information on options see envoy/ci/do_ci.sh) 29 | 30 | ```bash 31 | sudo IMAGE_NAME=envoyproxy/envoy-build-ubuntu IMAGE_ID=modsecurity_v1 ./ci/run_envoy_docker.sh './ci/do_envoy_ci.sh bazel.release' 32 | ``` -------------------------------------------------------------------------------- /ci/WORKSPACE.filter.example: -------------------------------------------------------------------------------- 1 | workspace(name = "envoy_filter_example") 2 | 3 | local_repository( 4 | name = "envoy", 5 | path = "/source", 6 | ) 7 | 8 | load("@envoy//bazel:api_repositories.bzl", "envoy_api_dependencies") 9 | envoy_api_dependencies() 10 | 11 | load("@envoy//bazel:repositories.bzl", "envoy_dependencies", "GO_VERSION") 12 | load("@envoy//bazel:cc_configure.bzl", "cc_configure") 13 | 14 | envoy_dependencies() 15 | 16 | # TODO(htuch): Roll this into envoy_dependencies() 17 | load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies") 18 | rules_foreign_cc_dependencies() 19 | 20 | cc_configure() 21 | 22 | load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") 23 | go_rules_dependencies() 24 | go_register_toolchains(go_version = GO_VERSION) 25 | -------------------------------------------------------------------------------- /ci/build_container/Dockerfile-centos: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | COPY ./envoy/ci/build_container_common.sh / 4 | COPY ./envoy/ci/build_container_centos.sh / 5 | 6 | ENV PATH /opt/rh/rh-git218/root/usr/bin:/opt/rh/devtoolset-7/root/usr/bin:/opt/llvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 7 | RUN ./build_container_centos.sh 8 | -------------------------------------------------------------------------------- /ci/build_container/Dockerfile-ubuntu: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | 3 | COPY ./build_container_common.sh / 4 | COPY ./build_container_ubuntu.sh / 5 | RUN ./build_container_ubuntu.sh 6 | 7 | COPY ./build_container_modsecurity_ubuntu.sh / 8 | RUN ./build_container_modsecurity_ubuntu.sh 9 | -------------------------------------------------------------------------------- /ci/build_container/build_container_modsecurity_ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Requirements by ModSecurity 5 | apt-get update 6 | export DEBIAN_FRONTEND=noninteractive 7 | apt-get install -y libtool cmake realpath clang-format-5.0 automake 8 | apt-get install -y g++ flex bison curl doxygen libyajl-dev libgeoip-dev libtool dh-autoreconf libcurl4-gnutls-dev libxml2 libpcre++-dev libxml2-dev -------------------------------------------------------------------------------- /ci/build_container/docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Temporarily copy files from envoy 4 | BUILD_FILES=`ls $(dirname $0)/../../envoy/ci/build_container/build_container*.sh` 5 | echo "Copying build files from envoy's git submodule: $BUILD_FILES" 6 | for file in $BUILD_FILES; do 7 | cp $file . 8 | done 9 | 10 | cleanup() { 11 | # Cleanup when finished 12 | echo "Cleaning up temporary files" 13 | for file in $BUILD_FILES; do 14 | rm `basename $file` 15 | done 16 | } 17 | 18 | trap cleanup EXIT 19 | 20 | [[ -z "${LINUX_DISTRO}" ]] && LINUX_DISTRO="ubuntu" 21 | [[ -z "${IMAGE_NAME}" ]] && IMAGE_NAME=envoyproxy/envoy-build-"${LINUX_DISTRO}" 22 | 23 | docker build -f Dockerfile-${LINUX_DISTRO} -t ${IMAGE_NAME}:$CIRCLE_SHA1 . 24 | 25 | -------------------------------------------------------------------------------- /ci/do_ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | export PATH=/usr/lib/llvm-8/bin:$PATH 4 | export CC=clang 5 | export CXX=clang++ 6 | export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-7/bin/llvm-symbolizer 7 | echo "$CC/$CXX toolchain configured" 8 | 9 | if [[ -f "${HOME:-/root}/.gitconfig" ]]; then 10 | mv "${HOME:-/root}/.gitconfig" "${HOME:-/root}/.gitconfig_save" 11 | fi 12 | 13 | function do_build () { 14 | bazel build -c opt --verbose_failures=true //:envoy 15 | } 16 | 17 | function do_test() { 18 | bazel test --test_output=all --test_env=ENVOY_IP_TEST_VERSIONS=v4only \ 19 | //:echo2_integration_test 20 | } 21 | 22 | case "$1" in 23 | build) 24 | do_build 25 | ;; 26 | test) 27 | do_test 28 | ;; 29 | *) 30 | echo "must be one of [build,test]" 31 | exit 1 32 | ;; 33 | esac 34 | -------------------------------------------------------------------------------- /ci/do_envoy_ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script executes envoy's do_ci.sh 4 | # However since the bazel target is //:envoy-static instead of //source/exe:envoy-static 5 | # We use sed to rewrite those targets and execute the modified script 6 | 7 | cd "$(dirname "$0")"/../envoy/ci 8 | 9 | # Set $0 to match the envoy's do_ci.sh 10 | /bin/bash -c "eval `sed 's#source/exe##g' ./do_ci.sh`" "./do_ci.sh" $* 11 | -------------------------------------------------------------------------------- /ci/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | 4 | # if the first argument look like a parameter (i.e. start with '-'), run Envoy 5 | if [ "${1#-}" != "$1" ]; then 6 | set -- envoy "$@" 7 | fi 8 | 9 | if [ "$1" = 'envoy' ]; then 10 | # set the log level if the $loglevel variable is set 11 | if [ -n "$loglevel" ]; then 12 | set -- "$@" --log-level "$loglevel" 13 | fi 14 | fi 15 | 16 | exec "$@" 17 | -------------------------------------------------------------------------------- /ci/docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | docker build -f ci/Dockerfile-envoy-image -t envoyproxy/envoy-dev:latest . 6 | #docker build -f ci/Dockerfile-envoy-alpine -t envoyproxy/envoy-alpine-dev:latest . 7 | #docker build -f ci/Dockerfile-envoy-alpine-debug -t envoyproxy/envoy-alpine-debug-dev:latest . 8 | -------------------------------------------------------------------------------- /ci/run_envoy_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | . envoy/ci/envoy_build_sha.sh 6 | 7 | # We run as root and later drop permissions. This is required to setup the USER 8 | # in useradd below, which is need for correct Python execution in the Docker 9 | # environment. 10 | USER=root 11 | USER_GROUP=root 12 | 13 | [[ -z "${IMAGE_NAME}" ]] && IMAGE_NAME="envoyproxy/envoy-build-ubuntu" 14 | # The IMAGE_ID defaults to the CI hash but can be set to an arbitrary image ID (found with 'docker 15 | # images'). 16 | [[ -z "${IMAGE_ID}" ]] && IMAGE_ID="${ENVOY_BUILD_SHA}" 17 | [[ -z "${ENVOY_DOCKER_BUILD_DIR}" ]] && ENVOY_DOCKER_BUILD_DIR=/tmp/envoy-docker-build 18 | 19 | [[ -f .git ]] && [[ ! -d .git ]] && GIT_VOLUME_OPTION="-v $(git rev-parse --git-common-dir):$(git rev-parse --git-common-dir)" 20 | 21 | mkdir -p "${ENVOY_DOCKER_BUILD_DIR}" 22 | # Since we specify an explicit hash, docker-run will pull from the remote repo if missing. 23 | docker run --rm -t -i \ 24 | -e HTTP_PROXY=${http_proxy} \ 25 | -e HTTPS_PROXY=${https_proxy} \ 26 | -u "${USER}":"${USER_GROUP}" \ 27 | -v "${ENVOY_DOCKER_BUILD_DIR}":/build \ 28 | ${GIT_VOLUME_OPTION} \ 29 | -v "$PWD":/source \ 30 | -v "$(realpath $PWD/modsecurity/include)":/source/modsecurity/include \ 31 | -v "$(realpath $PWD/modsecurity/libmodsecurity.a)":/source/modsecurity/libmodsecurity.a \ 32 | -e NUM_CPUS \ 33 | --cap-add SYS_PTRACE \ 34 | --cap-add NET_RAW \ 35 | --cap-add NET_ADMIN \ 36 | "${IMAGE_NAME}":"${IMAGE_ID}" \ 37 | /bin/bash -lc "groupadd --gid $(id -g) -f envoygroup && useradd -o --uid $(id -u) --gid $(id -g) --no-create-home \ 38 | --home-dir /source envoybuild && usermod -a -G pcap envoybuild && su envoybuild -c \"cd source && $*\"" 39 | -------------------------------------------------------------------------------- /conf/envoy-modsecurity-example-lds.yaml: -------------------------------------------------------------------------------- 1 | static_resources: 2 | clusters: 3 | - name: service1 4 | connect_timeout: 8s 5 | type: static 6 | hosts: 7 | - socket_address: 8 | address: 127.0.0.1 9 | port_value: 5555 10 | - name: service2 11 | connect_timeout: 8s 12 | type: static 13 | hosts: 14 | - socket_address: 15 | address: 127.0.0.1 16 | port_value: 10000 17 | 18 | dynamic_resources: 19 | lds_config: 20 | path: conf/lds.yaml 21 | 22 | admin: 23 | access_log_path: "/dev/null" 24 | address: 25 | socket_address: 26 | address: 0.0.0.0 27 | port_value: 8001 28 | -------------------------------------------------------------------------------- /conf/envoy-modsecurity-example.yaml: -------------------------------------------------------------------------------- 1 | static_resources: 2 | listeners: 3 | - address: 4 | socket_address: 5 | address: 0.0.0.0 6 | port_value: 8585 7 | filter_chains: 8 | - filters: 9 | - name: envoy.http_connection_manager 10 | config: 11 | codec_type: auto 12 | stat_prefix: ingress_http 13 | route_config: 14 | name: local_route 15 | virtual_hosts: 16 | - name: backend 17 | domains: 18 | - "*" 19 | routes: 20 | - match: 21 | prefix: "/" 22 | route: 23 | cluster: service1 24 | metadata: 25 | filter_metadata: 26 | envoy.filters.http.modsecurity: 27 | # disable: true 28 | # disable_request: true 29 | # disable_response: true 30 | http_filters: 31 | - name: envoy.filters.http.modsecurity # before envoy.router because order matters! 32 | config: 33 | rules: conf/modsecurity.conf 34 | - name: envoy.router 35 | config: {} 36 | clusters: 37 | - name: service1 38 | connect_timeout: 8s 39 | type: logical_dns 40 | lb_policy: round_robin 41 | hosts: 42 | - socket_address: 43 | address: 127.0.0.1 44 | port_value: 5555 45 | admin: 46 | access_log_path: "/dev/null" 47 | address: 48 | socket_address: 49 | address: 0.0.0.0 50 | port_value: 8001 51 | -------------------------------------------------------------------------------- /conf/lds.yaml: -------------------------------------------------------------------------------- 1 | version_info: "1" 2 | resources: 3 | - "@type": type.googleapis.com/envoy.api.v2.Listener 4 | name: listener_0 5 | address: 6 | socket_address: 7 | address: 0.0.0.0 8 | port_value: 8585 9 | filter_chains: 10 | - filters: 11 | - name: envoy.http_connection_manager 12 | config: 13 | codec_type: auto 14 | stat_prefix: ingress_http 15 | route_config: 16 | name: local_route 17 | virtual_hosts: 18 | - name: backend 19 | domains: 20 | - "*" 21 | routes: 22 | - match: 23 | prefix: "/" 24 | route: 25 | cluster: service1 26 | metadata: 27 | filter_metadata: 28 | envoy.filters.http.modsecurity: 29 | # disable: true 30 | # disable_request: true 31 | # disable_response: true 32 | http_filters: 33 | # before envoy.router because order matters! 34 | - name: envoy.filters.http.modsecurity 35 | config: 36 | rules_inline: | 37 | Include "conf/modsecurity.v3.0.3.conf" 38 | Include "owasp-modsecurity-crs-3.1.1/crs-setup.conf.example" 39 | Include "owasp-modsecurity-crs-3.1.1/rules/*.conf" 40 | 41 | SecRuleEngine DetectionOnly 42 | SecRule ARGS:param1 "test" "id:1,phase:1,deny,status:400,msg:'Test rule'" 43 | SecRule REQUEST_BODY "blocktest" "id:2,phase:2,deny,status:400,msg:'Test rule'" 44 | webhook: 45 | http_uri: 46 | uri: http://localhost:10000/wh_callback 47 | cluster: service2 48 | timeout: 49 | seconds: 3 50 | secret: webhook_secret 51 | - name: envoy.router 52 | config: {} 53 | -------------------------------------------------------------------------------- /conf/modsecurity.conf: -------------------------------------------------------------------------------- 1 | # -- Rule engine initialization ---------------------------------------------- 2 | 3 | # Enable ModSecurity, attaching it to every transaction. Use detection 4 | # only to start with, because that minimises the chances of post-installation 5 | # disruption. 6 | # 7 | Include "modsecurity.v3.0.3.conf" 8 | Include "owasp-modsecurity-crs-3.1.1/crs-setup.conf.example" 9 | Include "owasp-modsecurity-crs-3.1.1/rules/*.conf" 10 | 11 | SecRuleEngine On 12 | SecRule ARGS:param1 "test" "id:1,phase:1,deny,status:400,msg:'Test rule'" 13 | SecRule REQUEST_BODY "blocktest" "id:2,phase:2,deny,status:400,msg:'Test rule'" 14 | -------------------------------------------------------------------------------- /conf/modsecurity.v3.0.3.conf: -------------------------------------------------------------------------------- 1 | # -- Rule engine initialization ---------------------------------------------- 2 | 3 | # Enable ModSecurity, attaching it to every transaction. Use detection 4 | # only to start with, because that minimises the chances of post-installation 5 | # disruption. 6 | # 7 | SecRuleEngine DetectionOnly 8 | 9 | 10 | # -- Request body handling --------------------------------------------------- 11 | 12 | # Allow ModSecurity to access request bodies. If you don't, ModSecurity 13 | # won't be able to see any POST parameters, which opens a large security 14 | # hole for attackers to exploit. 15 | # 16 | SecRequestBodyAccess On 17 | 18 | 19 | # Enable XML request body parser. 20 | # Initiate XML Processor in case of xml content-type 21 | # 22 | SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \ 23 | "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" 24 | 25 | # Enable JSON request body parser. 26 | # Initiate JSON Processor in case of JSON content-type; change accordingly 27 | # if your application does not use 'application/json' 28 | # 29 | SecRule REQUEST_HEADERS:Content-Type "application/json" \ 30 | "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" 31 | 32 | # Maximum request body size we will accept for buffering. If you support 33 | # file uploads then the value given on the first line has to be as large 34 | # as the largest file you are willing to accept. The second value refers 35 | # to the size of data, with files excluded. You want to keep that value as 36 | # low as practical. 37 | # 38 | SecRequestBodyLimit 13107200 39 | SecRequestBodyNoFilesLimit 131072 40 | 41 | # What do do if the request body size is above our configured limit. 42 | # Keep in mind that this setting will automatically be set to ProcessPartial 43 | # when SecRuleEngine is set to DetectionOnly mode in order to minimize 44 | # disruptions when initially deploying ModSecurity. 45 | # 46 | SecRequestBodyLimitAction Reject 47 | 48 | # Verify that we've correctly processed the request body. 49 | # As a rule of thumb, when failing to process a request body 50 | # you should reject the request (when deployed in blocking mode) 51 | # or log a high-severity alert (when deployed in detection-only mode). 52 | # 53 | SecRule REQBODY_ERROR "!@eq 0" \ 54 | "id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" 55 | 56 | # By default be strict with what we accept in the multipart/form-data 57 | # request body. If the rule below proves to be too strict for your 58 | # environment consider changing it to detection-only. You are encouraged 59 | # _not_ to remove it altogether. 60 | # 61 | SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ 62 | "id:'200003',phase:2,t:none,log,deny,status:400, \ 63 | msg:'Multipart request body failed strict validation: \ 64 | PE %{REQBODY_PROCESSOR_ERROR}, \ 65 | BQ %{MULTIPART_BOUNDARY_QUOTED}, \ 66 | BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ 67 | DB %{MULTIPART_DATA_BEFORE}, \ 68 | DA %{MULTIPART_DATA_AFTER}, \ 69 | HF %{MULTIPART_HEADER_FOLDING}, \ 70 | LF %{MULTIPART_LF_LINE}, \ 71 | SM %{MULTIPART_MISSING_SEMICOLON}, \ 72 | IQ %{MULTIPART_INVALID_QUOTING}, \ 73 | IP %{MULTIPART_INVALID_PART}, \ 74 | IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ 75 | FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" 76 | 77 | # Did we see anything that might be a boundary? 78 | # 79 | # Here is a short description about the ModSecurity Multipart parser: the 80 | # parser returns with value 0, if all "boundary-like" line matches with 81 | # the boundary string which given in MIME header. In any other cases it returns 82 | # with different value, eg. 1 or 2. 83 | # 84 | # The RFC 1341 descript the multipart content-type and its syntax must contains 85 | # only three mandatory lines (above the content): 86 | # * Content-Type: multipart/mixed; boundary=BOUNDARY_STRING 87 | # * --BOUNDARY_STRING 88 | # * --BOUNDARY_STRING-- 89 | # 90 | # First line indicates, that this is a multipart content, second shows that 91 | # here starts a part of the multipart content, third shows the end of content. 92 | # 93 | # If there are any other lines, which starts with "--", then it should be 94 | # another boundary id - or not. 95 | # 96 | # After 3.0.3, there are two kinds of types of boundary errors: strict and permissive. 97 | # 98 | # If multipart content contains the three necessary lines with correct order, but 99 | # there are one or more lines with "--", then parser returns with value 2 (non-zero). 100 | # 101 | # If some of the necessary lines (usually the start or end) misses, or the order 102 | # is wrong, then parser returns with value 1 (also a non-zero). 103 | # 104 | # You can choose, which one is what you need. The example below contains the 105 | # 'strict' mode, which means if there are any lines with start of "--", then 106 | # ModSecurity blocked the content. But the next, commented example contains 107 | # the 'permissive' mode, then you check only if the necessary lines exists in 108 | # correct order. Whit this, you can enable to upload PEM files (eg "----BEGIN.."), 109 | # or other text files, which contains eg. HTTP headers. 110 | # 111 | # The difference is only the operator - in strict mode (first) the content blocked 112 | # in case of any non-zero value. In permissive mode (second, commented) the 113 | # content blocked only if the value is explicit 1. If it 0 or 2, the content will 114 | # allowed. 115 | # 116 | 117 | # 118 | # See #1747 and #1924 for further information on the possible values for 119 | # MULTIPART_UNMATCHED_BOUNDARY. 120 | # 121 | SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \ 122 | "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" 123 | 124 | 125 | # PCRE Tuning 126 | # We want to avoid a potential RegEx DoS condition 127 | # 128 | SecPcreMatchLimit 1000 129 | SecPcreMatchLimitRecursion 1000 130 | 131 | # Some internal errors will set flags in TX and we will need to look for these. 132 | # All of these are prefixed with "MSC_". The following flags currently exist: 133 | # 134 | # MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. 135 | # 136 | SecRule TX:/^MSC_/ "!@streq 0" \ 137 | "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" 138 | 139 | 140 | # -- Response body handling -------------------------------------------------- 141 | 142 | # Allow ModSecurity to access response bodies. 143 | # You should have this directive enabled in order to identify errors 144 | # and data leakage issues. 145 | # 146 | # Do keep in mind that enabling this directive does increases both 147 | # memory consumption and response latency. 148 | # 149 | SecResponseBodyAccess On 150 | 151 | # Which response MIME types do you want to inspect? You should adjust the 152 | # configuration below to catch documents but avoid static files 153 | # (e.g., images and archives). 154 | # 155 | SecResponseBodyMimeType text/plain text/html text/xml 156 | 157 | # Buffer response bodies of up to 512 KB in length. 158 | SecResponseBodyLimit 524288 159 | 160 | # What happens when we encounter a response body larger than the configured 161 | # limit? By default, we process what we have and let the rest through. 162 | # That's somewhat less secure, but does not break any legitimate pages. 163 | # 164 | SecResponseBodyLimitAction ProcessPartial 165 | 166 | 167 | # -- Filesystem configuration ------------------------------------------------ 168 | 169 | # The location where ModSecurity stores temporary files (for example, when 170 | # it needs to handle a file upload that is larger than the configured limit). 171 | # 172 | # This default setting is chosen due to all systems have /tmp available however, 173 | # this is less than ideal. It is recommended that you specify a location that's private. 174 | # 175 | SecTmpDir /tmp/ 176 | 177 | # The location where ModSecurity will keep its persistent data. This default setting 178 | # is chosen due to all systems have /tmp available however, it 179 | # too should be updated to a place that other users can't access. 180 | # 181 | SecDataDir /tmp/ 182 | 183 | 184 | # -- File uploads handling configuration ------------------------------------- 185 | 186 | # The location where ModSecurity stores intercepted uploaded files. This 187 | # location must be private to ModSecurity. You don't want other users on 188 | # the server to access the files, do you? 189 | # 190 | #SecUploadDir /opt/modsecurity/var/upload/ 191 | 192 | # By default, only keep the files that were determined to be unusual 193 | # in some way (by an external inspection script). For this to work you 194 | # will also need at least one file inspection rule. 195 | # 196 | #SecUploadKeepFiles RelevantOnly 197 | 198 | # Uploaded files are by default created with permissions that do not allow 199 | # any other user to access them. You may need to relax that if you want to 200 | # interface ModSecurity to an external program (e.g., an anti-virus). 201 | # 202 | #SecUploadFileMode 0600 203 | 204 | 205 | # -- Debug log configuration ------------------------------------------------- 206 | 207 | # The default debug log configuration is to duplicate the error, warning 208 | # and notice messages from the error log. 209 | # 210 | #SecDebugLog /opt/modsecurity/var/log/debug.log 211 | #SecDebugLogLevel 3 212 | 213 | 214 | # -- Audit log configuration ------------------------------------------------- 215 | 216 | # Log the transactions that are marked by a rule, as well as those that 217 | # trigger a server error (determined by a 5xx or 4xx, excluding 404, 218 | # level response status codes). 219 | # 220 | SecAuditEngine RelevantOnly 221 | SecAuditLogRelevantStatus "^(?:5|4(?!04))" 222 | 223 | # Log everything we know about a transaction. 224 | SecAuditLogParts ABIJDEFHZ 225 | 226 | # Use a single file for logging. This is much easier to look at, but 227 | # assumes that you will use the audit log only ocassionally. 228 | # 229 | SecAuditLogType Serial 230 | SecAuditLog /var/log/modsec_audit.log 231 | 232 | # Specify the path for concurrent audit logging. 233 | #SecAuditLogStorageDir /opt/modsecurity/var/audit/ 234 | 235 | 236 | # -- Miscellaneous ----------------------------------------------------------- 237 | 238 | # Use the most commonly used application/x-www-form-urlencoded parameter 239 | # separator. There's probably only one application somewhere that uses 240 | # something else so don't expect to change this value. 241 | # 242 | SecArgumentSeparator & 243 | 244 | # Settle on version 0 (zero) cookies, as that is what most applications 245 | # use. Using an incorrect cookie version may open your installation to 246 | # evasion attacks (against the rules that examine named cookies). 247 | # 248 | SecCookieFormat 0 249 | 250 | # Specify your Unicode Code Point. 251 | # This mapping is used by the t:urlDecodeUni transformation function 252 | # to properly map encoded data to your language. Properly setting 253 | # these directives helps to reduce false positives and negatives. 254 | # 255 | SecUnicodeMapFile unicode.mapping 20127 256 | 257 | # Improve the quality of ModSecurity by sharing information about your 258 | # current ModSecurity version and dependencies versions. 259 | # The following information will be shared: ModSecurity version, 260 | # Web Server version, APR version, PCRE version, Lua version, Libxml2 261 | # version, Anonymous unique id for host. 262 | SecStatusEngine Off 263 | 264 | -------------------------------------------------------------------------------- /conf/unicode.mapping: -------------------------------------------------------------------------------- 1 | (MAC - Roman) 2 | 3 | 4 | (MAC - Icelandic) 5 | 6 | 7 | 1250 (ANSI - Central Europe) 8 | 00a1:21 00a2:63 00a3:4c 00a5:59 00aa:61 00b2:32 00b3:33 00b9:31 00ba:6f 00bc:31 00bd:31 00be:33 00c0:41 00c3:41 00c5:41 00c6:41 00c8:45 00ca:45 00cc:49 00cf:49 00d1:4e 00d2:4f 00d5:4f 00d8:4f 00d9:55 00db:55 00e0:61 00e3:61 00e5:61 00e6:61 00e8:65 00ea:65 00ec:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f8:6f 00f9:75 00fb:75 00ff:79 0100:41 0101:61 0108:43 0109:63 010a:43 010b:63 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 013b:4c 013c:6c 0145:4e 0146:6e 014c:4f 014d:6f 014e:4f 014f:6f 0152:4f 0153:6f 0156:52 0157:72 015c:53 015d:73 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0180:62 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2032:27 2035:60 203c:21 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2191:5e 2194:2d 2195:7c 21a8:7c 2212:2d 2215:2f 2216:5c 2217:2a 221f:4c 2223:7c 2236:3a 223c:7e 2303:5e 2329:3c 232a:3e 2502:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263c:30 2640:2b 2642:3e 266a:64 266b:64 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 9 | 10 | 1251 (ANSI - Cyrillic) 11 | 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 203c:21 2190:3c 2191:5e 2192:3e 2193:76 2194:2d 221a:76 221f:4c 2500:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2552:2d 2558:4c 2559:4c 255a:4c 255b:2d 255c:2d 255d:2d 2564:54 2565:54 2566:54 256a:2b 256b:2b 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 12 | 13 | 1252 (ANSI - Latin I) 14 | 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c8:27 02cb:60 02cd:5f 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 0393:47 0398:54 03a3:53 03a6:46 03a9:4f 03b1:61 03b4:64 03b5:65 03c0:70 03c3:73 03c4:74 03c6:66 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2017:3d 2032:27 2035:60 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 207f:6e 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2212:2d 2215:2f 2216:5c 2217:2a 221a:76 221e:38 2223:7c 2229:6e 2236:3a 223c:7e 2261:3d 2264:3d 2265:3d 2303:5e 2320:28 2321:29 2329:3c 232a:3e 2500:2d 250c:2b 2510:2b 2514:2b 2518:2b 251c:2b 252c:2d 2534:2d 253c:2b 2550:2d 2552:2b 2553:2b 2554:2b 2555:2b 2556:2b 2557:2b 2558:2b 2559:2b 255a:2b 255b:2b 255c:2b 255d:2b 2564:2d 2565:2d 2566:2d 2567:2d 2568:2d 2569:2d 256a:2b 256b:2b 256c:2b 2584:5f 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 15 | 16 | 1253 (ANSI - Greek) 17 | 00b4:2f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 037e:3b 203c:21 2190:3c 2191:5e 2192:3e 2193:76 2194:2d 221f:4c 2500:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 18 | 19 | 1254 (ANSI - Turkish) 20 | 00dd:59 00fd:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c7:5e 02c8:27 02cb:60 02cd:5f 02d8:5e 02d9:27 0300:60 0302:5e 0331:5f 0332:5f 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2032:27 2035:60 203c:21 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2081:30 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2191:5e 2193:76 2194:2d 2195:7c 21a8:7c 2212:2d 2215:2f 2216:5c 2217:2a 221f:4c 2223:7c 2236:3a 223c:7e 2303:5e 2329:3c 232a:3e 2502:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:3d 301d:22 301e:22 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 21 | 22 | 1255 (ANSI - Hebrew) 23 | 0191:46 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 24 | 25 | 1256 (ANSI - Arabic) 26 | 0620:41 0621:41 0622:43 0623:45 0624:45 0625:45 0626:45 0627:49 0628:49 0629:4f 062a:55 062b:55 062c:55 062d:46 062e:43 062f:44 0630:45 0631:46 0632:47 0633:48 0634:49 0635:4a 0636:4b 0637:4c 0638:4d 0639:4e 063a:4f 0641:41 0642:42 0643:43 0644:44 0645:45 0646:46 0647:47 0648:48 0649:49 064a:4a 064b:4b 064c:4c 064d:4d 064e:4e 064f:4f 0650:50 0651:51 0652:52 27 | 28 | 1257 (ANSI - Baltic) 29 | ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 30 | 31 | 1258 (ANSI/OEM - Viet Nam) 32 | ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 33 | 34 | 20127 (US-ASCII) 35 | 00a0:20 00a1:21 00a2:63 00a4:24 00a5:59 00a6:7c 00a9:43 00aa:61 00ab:3c 00ad:2d 00ae:52 00b2:32 00b3:33 00b7:2e 00b8:2c 00b9:31 00ba:6f 00bb:3e 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 36 | 37 | 20261 (T.61) 38 | f8dd:5c f8de:5e f8df:60 f8e0:7b f8fc:7d f8fd:7e f8fe:7f 39 | 40 | 20866 (Russian - KOI8) 41 | 00a7:15 00ab:3c 00ad:2d 00ae:52 00b1:2b 00b6:14 00bb:3e 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2026:3a 2030:25 2039:3c 203a:3e 203c:13 2122:54 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 221f:1c 2302:7f 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 42 | 43 | 28591 (ISO 8859-1 Latin I) 44 | 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 45 | 46 | 28592 (ISO 8859-2 Central Europe) 47 | 00a1:21 00a2:63 00a5:59 00a6:7c 00a9:43 00aa:61 00ab:3c 00ae:52 00b2:32 00b3:33 00b7:2e 00b9:31 00ba:6f 00bb:3e 00c0:41 00c3:41 00c5:41 00c6:41 00c8:45 00ca:45 00cc:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d5:4f 00d8:4f 00d9:55 00db:55 00e0:61 00e3:61 00e5:61 00e6:61 00e8:65 00ea:65 00ec:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f8:6f 00f9:75 00fb:75 00ff:79 0100:41 0101:61 0108:43 0109:63 010a:43 010b:63 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 013b:4c 013c:6c 0145:4e 0146:6e 014c:4f 014d:6f 014e:4f 014f:6f 0152:4f 0153:6f 0156:52 0157:72 015c:53 015d:73 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 48 | 49 | 28605 (ISO 8859-15 Latin 9) 50 | 00a6:7c 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0138:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014a:4e 014b:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:54 0169:74 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 51 | 52 | 37 (IBM EBCDIC - U.S./Canada) 53 | 0004:37 0005:2d 0006:2e 0007:2f 0008:16 0009:05 000a:25 0014:3c 0015:3d 0016:32 0017:26 001a:3f 001b:27 0020:40 0021:5a 0022:7f 0023:7b 0024:5b 0025:6c 0026:50 0027:7d 0028:4d 0029:5d 002a:5c 002b:4e 002c:6b 002d:60 002e:4b 002f:61 003a:7a 003b:5e 003c:4c 003d:7e 003e:6e 003f:6f 0040:7c 005f:6d 0060:79 007c:4f 007f:07 0080:20 0081:21 0082:22 0083:23 0084:24 0085:15 0086:06 0087:17 0088:28 0089:29 008a:2a 008b:2b 008c:2c 008d:09 008e:0a 008f:1b 0090:30 0091:31 0092:1a 0093:33 0094:34 0095:35 0096:36 0097:08 0098:38 0099:39 009a:3a 009b:3b 009c:04 009d:14 009e:3e 00a0:41 00a2:4a 00a6:6a 00ac:5f 00c0:64 00c1:65 00c2:62 00c3:66 00c4:63 00c5:67 00c7:68 00c8:74 00c9:71 00ca:72 00cb:73 00cc:78 00cd:75 00ce:76 00cf:77 00d1:69 00df:59 00e0:44 00e1:45 00e2:42 00e3:46 00e4:43 00e5:47 00e7:48 00e8:54 00e9:51 00ea:52 00eb:53 00ec:58 00ed:55 00ee:56 00ef:57 00f1:49 00f8:70 ff01:5a ff02:7f ff03:7b ff04:5b ff05:6c ff06:50 ff07:7d ff08:4d ff09:5d ff0a:5c ff0b:4e ff0c:6b ff0d:60 ff0e:4b ff0f:61 ff1a:7a ff1b:5e ff1c:4c ff1d:7e ff1e:6e ff20:7c ff3f:6d ff40:79 ff5c:4f 54 | 55 | 437 (OEM - United States) 56 | 00a4:0f 00a7:15 00a8:22 00a9:63 00ad:2d 00ae:72 00af:5f 00b3:33 00b4:27 00b6:14 00b8:2c 00b9:31 00be:5f 00c0:41 00c1:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d7:78 00d8:4f 00d9:55 00da:55 00db:55 00dd:59 00de:5f 00e3:61 00f0:64 00f5:6f 00f8:6f 00fd:79 00fe:5f 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02ca:27 02cb:60 02cd:5f 02dc:7e 0300:60 0301:27 0302:5e 0303:7e 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:60 2019:27 201a:2c 201c:22 201d:22 201e:2c 2020:2b 2022:07 2026:2e 2030:25 2032:27 2035:60 2039:3c 203a:3e 203c:13 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:09 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2122:54 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2212:2d 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 2758:7c 3000:20 3007:09 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 57 | 58 | 500 (IBM EBCDIC - International) 59 | 0004:37 0005:2d 0006:2e 0007:2f 0008:16 0009:05 000a:25 0014:3c 0015:3d 0016:32 0017:26 001a:3f 001b:27 0020:40 0021:4f 0022:7f 0023:7b 0024:5b 0025:6c 0026:50 0027:7d 0028:4d 0029:5d 002a:5c 002b:4e 002c:6b 002d:60 002e:4b 002f:61 003a:7a 003b:5e 003c:4c 003d:7e 003e:6e 003f:6f 0040:7c 005b:4a 005d:5a 005e:5f 005f:6d 0060:79 007f:07 0080:20 0081:21 0082:22 0083:23 0084:24 0085:15 0086:06 0087:17 0088:28 0089:29 008a:2a 008b:2b 008c:2c 008d:09 008e:0a 008f:1b 0090:30 0091:31 0092:1a 0093:33 0094:34 0095:35 0096:36 0097:08 0098:38 0099:39 009a:3a 009b:3b 009c:04 009d:14 009e:3e 00a0:41 00a6:6a 00c0:64 00c1:65 00c2:62 00c3:66 00c4:63 00c5:67 00c7:68 00c8:74 00c9:71 00ca:72 00cb:73 00cc:78 00cd:75 00ce:76 00cf:77 00d1:69 00df:59 00e0:44 00e1:45 00e2:42 00e3:46 00e4:43 00e5:47 00e7:48 00e8:54 00e9:51 00ea:52 00eb:53 00ec:58 00ed:55 00ee:56 00ef:57 00f1:49 00f8:70 ff01:4f ff02:7f ff03:7b ff04:5b ff05:6c ff06:50 ff07:7d ff08:4d ff09:5d ff0a:5c ff0b:4e ff0c:6b ff0d:60 ff0e:4b ff0f:61 ff1a:7a ff1b:5e ff1c:4c ff1d:7e ff1e:6e ff20:7c ff3b:4a ff3d:5a ff3e:5f ff3f:6d ff40:79 60 | 61 | 850 (OEM - Multilingual Latin I) 62 | 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01a9:53 01ab:74 01ae:54 01af:55 01b0:75 01b6:5a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:27 02cd:5f 02dc:7e 0300:27 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 0393:47 03a3:53 03a6:46 03a9:4f 03b1:61 03b4:64 03b5:65 03c0:70 03c3:73 03c4:74 03c6:66 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:2e 2030:25 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:39 207f:6e 2080:30 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2122:54 2124:5a 2126:4f 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2211:53 2212:2d 2215:2f 2216:2f 2217:2a 2219:07 221a:56 221e:38 221f:1c 2229:6e 2236:3a 223c:7e 2248:7e 2261:3d 2264:3d 2265:3d 2302:7f 2303:5e 2320:28 2321:29 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 2713:56 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 63 | 64 | 860 (OEM - Portuguese) 65 | 00a4:0f 00a5:59 00a7:15 00a8:22 00a9:63 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00be:33 00c4:41 00c5:41 00c6:41 00cb:45 00ce:49 00cf:49 00d0:44 00d6:4f 00d7:58 00d8:4f 00db:55 00dd:59 00de:54 00e4:61 00e5:61 00e6:61 00eb:65 00ee:69 00ef:69 00f0:64 00f6:6f 00f8:6f 00fb:75 00fd:79 00fe:74 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:5c 0161:7c 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 0278:66 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:5f 2011:5f 2013:5f 2014:5f 2017:5f 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:07 2024:07 2026:2e 2030:25 2032:27 2035:60 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212b:41 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 66 | 67 | 861 (OEM - Icelandic) 68 | 00a2:63 00a4:0f 00a5:59 00a7:15 00a8:22 00a9:63 00aa:61 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00ba:6f 00be:33 00c0:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d4:4f 00d5:4f 00d7:58 00d9:55 00db:55 00e3:61 00ec:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f9:75 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 0278:66 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 69 | 70 | 863 (OEM - Canadian French) 71 | 00a1:21 00a5:59 00a9:63 00aa:61 00ad:16 00ae:72 00b9:33 00ba:6f 00c1:41 00c3:41 00c4:41 00c5:41 00c6:41 00cc:49 00cd:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d5:4f 00d6:4f 00d7:58 00d8:4f 00da:55 00dd:59 00de:54 00e1:61 00e3:61 00e4:61 00e5:61 00e6:61 00ec:69 00ed:69 00f0:64 00f1:6e 00f2:6f 00f5:6f 00f6:6f 00f8:6f 00fd:79 00fe:74 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:22 02ba:27 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 0304:16 0305:16 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212b:41 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 72 | 73 | 865 (OEM - Nordic) 74 | 00a2:63 00a5:59 00a7:15 00a8:22 00a9:63 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00bb:3e 00be:33 00c0:41 00c1:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d7:58 00d9:55 00da:55 00db:55 00dd:59 00de:54 00e3:61 00f0:64 00f5:6f 00fd:79 00fe:74 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 226b:3c 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 300b:3e 301a:5b 301b:5d 30fb:07 75 | 76 | 874 (ANSI/OEM - Thai) 77 | 00a7:15 00b6:14 203c:13 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 221f:1c 2302:7f 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 78 | 79 | 932 (ANSI/OEM - Japanese Shift-JIS) 80 | 00a1:21 00a5:5c 00a6:7c 00a9:63 00aa:61 00ad:2d 00ae:52 00b2:32 00b3:33 00b9:31 00ba:6f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00de:54 00df:73 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f0:64 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00fe:74 00ff:79 81 | 82 | 936 (ANSI/OEM - Simplified Chinese GBK) 83 | 00a6:7c 00aa:61 00ad:2d 00b2:32 00b3:33 00b9:31 00ba:6f 00d0:44 00dd:59 00de:54 00e2:61 00f0:65 00fd:79 00fe:74 84 | 85 | 949 (ANSI/OEM - Korean) 86 | 00a6:7c 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 20a9:5c 87 | 88 | 950 (ANSI/OEM - Traditional Chinese Big5) 89 | 00a1:21 00a6:7c 00a9:63 00aa:61 00ad:2d 00ae:52 00b2:32 00b3:33 00b9:31 00ba:6f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00de:54 00df:73 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f0:65 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00fe:74 00ff:79 90 | 91 | (UTF-7) 92 | 93 | 94 | (UTF-8) 95 | 96 | 97 | -------------------------------------------------------------------------------- /http-filter-modsecurity/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | load( 4 | "@envoy//bazel:envoy_build_system.bzl", 5 | "envoy_cc_binary", 6 | "envoy_cc_library", 7 | "envoy_cc_test", 8 | ) 9 | 10 | load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library") 11 | 12 | api_proto_library( 13 | name = "http_filter_proto", 14 | srcs = ["http_filter.proto"], 15 | deps = [ 16 | # TODO - "@envoy_api//envoy/api/v2/core:http_uri" (bad visibility) 17 | ] 18 | ) 19 | 20 | envoy_cc_library( 21 | name = "http_filter_lib", 22 | copts=["-Wno-unused-function", "-Wno-unused-parameter", "-Wno-reorder", "-Wno-unused-variable", "-Imodsecurity/include"], 23 | srcs = ["utility.cc", "http_filter.cc", "webhook_fetcher.cc"], 24 | hdrs = glob(["utility.h", "http_filter.h", "webhook_fetcher.h", "well_known_names.h"]), 25 | repository = "@envoy", 26 | deps = [ 27 | ":http_filter_proto_cc", 28 | "//:libmodsecurity", 29 | "@envoy//source/exe:envoy_common_lib", 30 | ], 31 | ) 32 | 33 | envoy_cc_library( 34 | name = "http_filter_config", 35 | srcs = ["http_filter_config.cc"], 36 | copts=["-Wno-unused-function", "-Wno-unused-parameter", "-Wno-reorder", "-Wno-unused-variable", "-Imodsecurity/include"], 37 | repository = "@envoy", 38 | deps = [ 39 | ":http_filter_lib", 40 | "@envoy//include/envoy/server:filter_config_interface" 41 | ], 42 | ) 43 | 44 | envoy_cc_test( 45 | name = "http_filter_integration_test", 46 | srcs = ["http_filter_integration_test.cc"], 47 | repository = "@envoy", 48 | deps = [ 49 | ":http_filter_config", 50 | "@envoy//test/integration:http_integration_lib", 51 | ], 52 | ) 53 | -------------------------------------------------------------------------------- /http-filter-modsecurity/README.md: -------------------------------------------------------------------------------- 1 | # Envoy filter example 2 | 3 | This project demonstrates the linking of additional HTTP filters with the Envoy binary. 4 | A new filter `sample` which adds a HTTP header is introduced. 5 | Integration tests demonstrating the filter's end-to-end behavior are 6 | also provided. 7 | 8 | ## Building 9 | 10 | To build the Envoy static binary: 11 | 12 | 1. `git submodule update --init` 13 | 2. `bazel build //:envoy` 14 | 15 | ## Testing 16 | 17 | To run the `sample` integration test: 18 | 19 | `bazel test //http-filter-example:http_filter_integration_test` 20 | 21 | ## How it works 22 | 23 | See the [network filter example](../README.md#how-it-works). 24 | 25 | ## How to write and use an HTTP filter 26 | 27 | - The main task is to write a class that implements the interface 28 | [`Envoy::Http::StreamDecoderFilter`][StreamDecoderFilter] as in 29 | [`http_filter.h`](http_filter.h) and [`http_filter.cc`](http_filter.cc), 30 | which contains functions that handle http headers, data, and trailers. 31 | Note that this is an example of decoder filters, 32 | and to write encoder filters or decoder/encoder filters 33 | you need to implement 34 | [`Envoy::Http::StreamEncoderFilter`][StreamEncoderFilter] or 35 | [`Envoy::Http::StreamFilter`][StreamFilter] instead. 36 | - You also need a class that implements 37 | `Envoy::Server::Configuration::NamedHttpFilterConfigFactory` 38 | to enable the Envoy binary to find your filter, 39 | as in [`http_filter_config.cc`](http_filter_config.cc). 40 | It should be linked to the Envoy binary by modifying [`BUILD`][BUILD] file. 41 | - Finally, you need to modify the Envoy config file to add your filter to the 42 | filter chain for a particular HTTP route configuration. For instance, if you 43 | wanted to change [the front-proxy example][front-envoy.yaml] to chain our 44 | `sample` filter, you'd need to modify its config to look like 45 | 46 | ```yaml 47 | http_filters: 48 | - name: sample # before envoy.router because order matters! 49 | config: 50 | key: via 51 | val: sample-filter 52 | - name: envoy.router 53 | config: {} 54 | ``` 55 | 56 | 57 | [StreamDecoderFilter]: https://github.com/envoyproxy/envoy/blob/b2610c84aeb1f75c804d67effcb40592d790e0f1/include/envoy/http/filter.h#L300 58 | [StreamEncoderFilter]: https://github.com/envoyproxy/envoy/blob/b2610c84aeb1f75c804d67effcb40592d790e0f1/include/envoy/http/filter.h#L413 59 | [StreamFilter]: https://github.com/envoyproxy/envoy/blob/b2610c84aeb1f75c804d67effcb40592d790e0f1/include/envoy/http/filter.h#L462 60 | [BUILD]: https://github.com/envoyproxy/envoy-filter-example/blob/d76d3096c4cbd647d26b44b3f801c3afbc81d3e2/http-filter-example/BUILD#L15-L18 61 | [front-envoy.yaml]: https://github.com/envoyproxy/envoy/blob/b2610c84aeb1f75c804d67effcb40592d790e0f1/examples/front-proxy/front-envoy.yaml#L28 62 | -------------------------------------------------------------------------------- /http-filter-modsecurity/http_filter.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "http_filter.h" 6 | #include "utility.h" 7 | 8 | #include "common/common/stack_array.h" 9 | #include "common/http/utility.h" 10 | #include "common/http/headers.h" 11 | #include "common/config/metadata.h" 12 | #include "envoy/server/filter_config.h" 13 | #include "common/json/json_loader.h" 14 | #include "modsecurity/rule_message.h" 15 | 16 | namespace Envoy { 17 | namespace Http { 18 | 19 | HttpModSecurityFilterConfig::HttpModSecurityFilterConfig(const modsecurity::ModsecurityFilterConfigDecoder& proto_config, 20 | Server::Configuration::FactoryContext& context) 21 | : rules_path_(proto_config.rules_path()), 22 | rules_inline_(proto_config.rules_inline()), 23 | webhook_(proto_config.webhook()), 24 | tls_(context.threadLocal().allocateSlot()) { 25 | 26 | modsec_.reset(new modsecurity::ModSecurity()); 27 | modsec_->setConnectorInformation("ModSecurity-test v0.0.1-alpha (ModSecurity test)"); 28 | modsec_->setServerLogCb(HttpModSecurityFilter::_logCb, modsecurity::RuleMessageLogProperty | 29 | modsecurity::IncludeFullHighlightLogProperty); 30 | 31 | modsec_rules_.reset(new modsecurity::Rules()); 32 | if (!rules_path().empty()) { 33 | int rulesLoaded = modsec_rules_->loadFromUri(rules_path().c_str()); 34 | ENVOY_LOG(debug, "Loading ModSecurity config from {}", rules_path()); 35 | if (rulesLoaded == -1) { 36 | ENVOY_LOG(error, "Failed to load rules: {}", modsec_rules_->getParserError()); 37 | } else { 38 | ENVOY_LOG(info, "Loaded {} rules", rulesLoaded); 39 | }; 40 | } 41 | if (!rules_inline().empty()) { 42 | int rulesLoaded = modsec_rules_->load(rules_inline().c_str()); 43 | ENVOY_LOG(debug, "Loading ModSecurity inline rules"); 44 | if (rulesLoaded == -1) { 45 | ENVOY_LOG(error, "Failed to load rules: {}", modsec_rules_->getParserError()); 46 | } else { 47 | ENVOY_LOG(info, "Loaded {} inline rules", rulesLoaded); 48 | }; 49 | } 50 | 51 | tls_->set([this, &context](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { 52 | return std::make_shared(new WebhookFetcher(context.clusterManager(), 53 | webhook_.http_uri(), 54 | webhook_.secret(), 55 | *this)); 56 | }); 57 | } 58 | 59 | HttpModSecurityFilterConfig::~HttpModSecurityFilterConfig() { 60 | } 61 | 62 | WebhookFetcherSharedPtr HttpModSecurityFilterConfig::webhook_fetcher() { 63 | return tls_->getTyped().webhook_fetcher_; 64 | } 65 | 66 | void HttpModSecurityFilterConfig::onSuccess(const Http::MessagePtr& response) { 67 | ENVOY_LOG(info, "webhook success!"); 68 | } 69 | void HttpModSecurityFilterConfig::onFailure(FailureReason reason) { 70 | ENVOY_LOG(info, "webhook failure!"); 71 | } 72 | 73 | 74 | HttpModSecurityFilter::HttpModSecurityFilter(HttpModSecurityFilterConfigSharedPtr config) 75 | : config_(config), intervined_(false), request_processed_(false), response_processed_(false) { 76 | 77 | modsec_transaction_.reset(new modsecurity::Transaction(config_->modsec_.get(), config_->modsec_rules_.get(), this)); 78 | } 79 | 80 | HttpModSecurityFilter::~HttpModSecurityFilter() { 81 | } 82 | 83 | 84 | void HttpModSecurityFilter::onDestroy() { 85 | modsec_transaction_->processLogging(); 86 | } 87 | 88 | const char* getProtocolString(const Protocol protocol) { 89 | switch (protocol) { 90 | case Protocol::Http10: 91 | return "1.0"; 92 | case Protocol::Http11: 93 | return "1.1"; 94 | case Protocol::Http2: 95 | return "2.0"; 96 | } 97 | NOT_REACHED_GCOVR_EXCL_LINE; 98 | } 99 | 100 | FilterHeadersStatus HttpModSecurityFilter::decodeHeaders(HeaderMap& headers, bool end_stream) { 101 | ENVOY_LOG(debug, "HttpModSecurityFilter::decodeHeaders"); 102 | if (intervined_ || request_processed_) { 103 | ENVOY_LOG(debug, "Processed"); 104 | return getRequestHeadersStatus(); 105 | } 106 | // TODO - do we want to support dynamicMetadata? 107 | const auto& metadata = decoder_callbacks_->route()->routeEntry()->metadata(); 108 | const auto& disable = Envoy::Config::Metadata::metadataValue(metadata, ModSecurityMetadataFilter::get().ModSecurity, MetadataModSecurityKey::get().Disable); 109 | const auto& disable_request = Envoy::Config::Metadata::metadataValue(metadata, ModSecurityMetadataFilter::get().ModSecurity, MetadataModSecurityKey::get().DisableRequest); 110 | if (disable_request.bool_value() || disable.bool_value()) { 111 | ENVOY_LOG(debug, "Filter disabled"); 112 | request_processed_ = true; 113 | return FilterHeadersStatus::Continue; 114 | } 115 | 116 | auto downstreamAddress = decoder_callbacks_->streamInfo().downstreamLocalAddress(); 117 | // TODO - Upstream is (always?) still not resolved in this stage. Use our local proxy's ip. Is this what we want? 118 | ASSERT(decoder_callbacks_->connection() != nullptr); 119 | auto localAddress = decoder_callbacks_->connection()->localAddress(); 120 | // According to documentation, downstreamAddress should never be nullptr 121 | ASSERT(downstreamAddress != nullptr); 122 | ASSERT(downstreamAddress->type() == Network::Address::Type::Ip); 123 | ASSERT(localAddress != nullptr); 124 | ASSERT(localAddress->type() == Network::Address::Type::Ip); 125 | modsec_transaction_->processConnection(downstreamAddress->ip()->addressAsString().c_str(), 126 | downstreamAddress->ip()->port(), 127 | localAddress->ip()->addressAsString().c_str(), 128 | localAddress->ip()->port()); 129 | if (intervention()) { 130 | return FilterHeadersStatus::StopIteration; 131 | } 132 | 133 | auto uri = headers.Path(); 134 | auto method = headers.Method(); 135 | modsec_transaction_->processURI(std::string(uri->value().getStringView()).c_str(), 136 | std::string(method->value().getStringView()).c_str(), 137 | getProtocolString(decoder_callbacks_->streamInfo().protocol().value_or(Protocol::Http11))); 138 | if (intervention()) { 139 | return FilterHeadersStatus::StopIteration; 140 | } 141 | 142 | headers.iterate( 143 | [](const HeaderEntry& header, void* context) -> HeaderMap::Iterate { 144 | 145 | std::string k = std::string(header.key().getStringView()); 146 | std::string v = std::string(header.value().getStringView()); 147 | static_cast(context)->modsec_transaction_->addRequestHeader(k.c_str(), v.c_str()); 148 | // TODO - does this special case makes sense? it doesn't exist on apache/nginx modsecurity bridges. 149 | // host header is cannonized to :authority even on http older than 2 150 | // see https://github.com/envoyproxy/envoy/issues/2209 151 | if (k == Headers::get().Host.get()) { 152 | static_cast(context)->modsec_transaction_->addRequestHeader(Headers::get().HostLegacy.get().c_str(), v.c_str()); 153 | } 154 | return HeaderMap::Iterate::Continue; 155 | }, 156 | this); 157 | modsec_transaction_->processRequestHeaders(); 158 | if (end_stream) { 159 | request_processed_ = true; 160 | } 161 | if (intervention()) { 162 | return FilterHeadersStatus::StopIteration; 163 | } 164 | return getRequestHeadersStatus(); 165 | } 166 | 167 | FilterDataStatus HttpModSecurityFilter::decodeData(Buffer::Instance& data, bool end_stream) { 168 | ENVOY_LOG(debug, "HttpModSecurityFilter::decodeData"); 169 | if (intervined_ || request_processed_) { 170 | ENVOY_LOG(debug, "Processed"); 171 | return getRequestStatus(); 172 | } 173 | 174 | uint64_t num_slices = data.getRawSlices(nullptr, 0); 175 | STACK_ARRAY(slices, Buffer::RawSlice, num_slices); 176 | data.getRawSlices(slices.begin(), num_slices); 177 | for (const Buffer::RawSlice& slice : slices) { 178 | size_t requestLen = modsec_transaction_->getRequestBodyLength(); 179 | // If append fails or append reached the limit, test for intervention (in case SecRequestBodyLimitAction is set to Reject) 180 | // Note, we can't rely solely on the return value of append, when SecRequestBodyLimitAction is set to Reject it returns true and sets the intervention 181 | if (modsec_transaction_->appendRequestBody(static_cast(slice.mem_), slice.len_) == false || 182 | (slice.len_ > 0 && requestLen == modsec_transaction_->getRequestBodyLength())) { 183 | ENVOY_LOG(debug, "HttpModSecurityFilter::decodeData appendRequestBody reached limit"); 184 | if (intervention()) { 185 | return FilterDataStatus::StopIterationNoBuffer; 186 | } 187 | // Otherwise set to process request 188 | end_stream = true; 189 | break; 190 | } 191 | } 192 | 193 | if (end_stream) { 194 | request_processed_ = true; 195 | modsec_transaction_->processRequestBody(); 196 | } 197 | if (intervention()) { 198 | return FilterDataStatus::StopIterationNoBuffer; 199 | } 200 | return getRequestStatus(); 201 | } 202 | 203 | FilterTrailersStatus HttpModSecurityFilter::decodeTrailers(HeaderMap&) { 204 | return FilterTrailersStatus::Continue; 205 | } 206 | 207 | void HttpModSecurityFilter::setDecoderFilterCallbacks(StreamDecoderFilterCallbacks& callbacks) { 208 | decoder_callbacks_ = &callbacks; 209 | } 210 | 211 | 212 | FilterHeadersStatus HttpModSecurityFilter::encodeHeaders(HeaderMap& headers, bool end_stream) { 213 | ENVOY_LOG(debug, "HttpModSecurityFilter::encodeHeaders"); 214 | if (intervined_ || response_processed_) { 215 | ENVOY_LOG(debug, "Processed"); 216 | return getResponseHeadersStatus(); 217 | } 218 | // TODO - do we want to support dynamicMetadata? 219 | const auto& metadata = encoder_callbacks_->route()->routeEntry()->metadata(); 220 | const auto& disable = Envoy::Config::Metadata::metadataValue(metadata, ModSecurityMetadataFilter::get().ModSecurity, MetadataModSecurityKey::get().Disable); 221 | const auto& disable_response = Envoy::Config::Metadata::metadataValue(metadata, ModSecurityMetadataFilter::get().ModSecurity, MetadataModSecurityKey::get().DisableResponse); 222 | if (disable.bool_value() || disable_response.bool_value()) { 223 | ENVOY_LOG(debug, "Filter disabled"); 224 | response_processed_ = true; 225 | return FilterHeadersStatus::Continue; 226 | } 227 | 228 | auto status = headers.Status(); 229 | uint64_t code = Utility::getResponseStatus(headers); 230 | headers.iterate( 231 | [](const HeaderEntry& header, void* context) -> HeaderMap::Iterate { 232 | static_cast(context)->modsec_transaction_->addResponseHeader( 233 | std::string(header.key().getStringView()).c_str(), 234 | std::string(header.value().getStringView()).c_str() 235 | ); 236 | return HeaderMap::Iterate::Continue; 237 | }, 238 | this); 239 | modsec_transaction_->processResponseHeaders(code, 240 | getProtocolString(encoder_callbacks_->streamInfo().protocol().value_or(Protocol::Http11))); 241 | 242 | if (intervention()) { 243 | return FilterHeadersStatus::StopIteration; 244 | } 245 | return getResponseHeadersStatus(); 246 | } 247 | 248 | FilterHeadersStatus HttpModSecurityFilter::encode100ContinueHeaders(HeaderMap& headers) { 249 | return FilterHeadersStatus::Continue; 250 | } 251 | 252 | FilterDataStatus HttpModSecurityFilter::encodeData(Buffer::Instance& data, bool end_stream) { 253 | ENVOY_LOG(debug, "HttpModSecurityFilter::encodeData"); 254 | if (intervined_ || response_processed_) { 255 | ENVOY_LOG(debug, "Processed"); 256 | return getResponseStatus(); 257 | } 258 | 259 | uint64_t num_slices = data.getRawSlices(nullptr, 0); 260 | STACK_ARRAY(slices, Buffer::RawSlice, num_slices); 261 | data.getRawSlices(slices.begin(), num_slices); 262 | for (const Buffer::RawSlice& slice : slices) { 263 | size_t responseLen = modsec_transaction_->getResponseBodyLength(); 264 | // If append fails or append reached the limit, test for intervention (in case SecResponseBodyLimitAction is set to Reject) 265 | // Note, we can't rely solely on the return value of append, when SecResponseBodyLimitAction is set to Reject it returns true and sets the intervention 266 | if (modsec_transaction_->appendResponseBody(static_cast(slice.mem_), slice.len_) == false || 267 | (slice.len_ > 0 && responseLen == modsec_transaction_->getResponseBodyLength())) { 268 | ENVOY_LOG(debug, "HttpModSecurityFilter::encodeData appendResponseBody reached limit"); 269 | if (intervention()) { 270 | return FilterDataStatus::StopIterationNoBuffer; 271 | } 272 | // Otherwise set to process response 273 | end_stream = true; 274 | break; 275 | } 276 | } 277 | 278 | if (end_stream) { 279 | response_processed_ = true; 280 | modsec_transaction_->processResponseBody(); 281 | } 282 | if (intervention()) { 283 | return FilterDataStatus::StopIterationNoBuffer; 284 | } 285 | return getResponseStatus(); 286 | } 287 | 288 | FilterTrailersStatus HttpModSecurityFilter::encodeTrailers(HeaderMap&) { 289 | return FilterTrailersStatus::Continue; 290 | } 291 | 292 | 293 | FilterMetadataStatus HttpModSecurityFilter::encodeMetadata(MetadataMap& metadata_map) { 294 | return FilterMetadataStatus::Continue; 295 | } 296 | 297 | void HttpModSecurityFilter::setEncoderFilterCallbacks(StreamEncoderFilterCallbacks& callbacks) { 298 | encoder_callbacks_ = &callbacks; 299 | } 300 | 301 | bool HttpModSecurityFilter::intervention() { 302 | if (!intervined_ && modsec_transaction_->m_it.disruptive) { 303 | // intervined_ must be set to true before sendLocalReply to avoid reentrancy when encoding the reply 304 | intervined_ = true; 305 | ENVOY_LOG(debug, "intervention"); 306 | decoder_callbacks_->sendLocalReply(static_cast(modsec_transaction_->m_it.status), 307 | "ModSecurity Action\n", 308 | [](Http::HeaderMap& headers) { 309 | }, absl::nullopt, ""); 310 | } 311 | return intervined_; 312 | } 313 | 314 | 315 | FilterHeadersStatus HttpModSecurityFilter::getRequestHeadersStatus() { 316 | if (intervined_) { 317 | ENVOY_LOG(debug, "StopIteration"); 318 | return FilterHeadersStatus::StopIteration; 319 | } 320 | if (request_processed_) { 321 | ENVOY_LOG(debug, "Continue"); 322 | return FilterHeadersStatus::Continue; 323 | } 324 | // If disruptive, hold until request_processed_, otherwise let the data flow. 325 | ENVOY_LOG(debug, "RuleEngine"); 326 | return modsec_transaction_->getRuleEngineState() == modsecurity::Rules::EnabledRuleEngine ? 327 | FilterHeadersStatus::StopIteration : 328 | FilterHeadersStatus::Continue; 329 | } 330 | 331 | FilterDataStatus HttpModSecurityFilter::getRequestStatus() { 332 | if (intervined_) { 333 | ENVOY_LOG(debug, "StopIterationNoBuffer"); 334 | return FilterDataStatus::StopIterationNoBuffer; 335 | } 336 | if (request_processed_) { 337 | ENVOY_LOG(debug, "Continue"); 338 | return FilterDataStatus::Continue; 339 | } 340 | // If disruptive, hold until request_processed_, otherwise let the data flow. 341 | ENVOY_LOG(debug, "RuleEngine"); 342 | return modsec_transaction_->getRuleEngineState() == modsecurity::Rules::EnabledRuleEngine ? 343 | FilterDataStatus::StopIterationAndBuffer : 344 | FilterDataStatus::Continue; 345 | } 346 | 347 | FilterHeadersStatus HttpModSecurityFilter::getResponseHeadersStatus() { 348 | if (intervined_ || response_processed_) { 349 | // If intervined, let encodeData return the localReply 350 | ENVOY_LOG(debug, "Continue"); 351 | return FilterHeadersStatus::Continue; 352 | } 353 | // If disruptive, hold until response_processed_, otherwise let the data flow. 354 | ENVOY_LOG(debug, "RuleEngine"); 355 | return modsec_transaction_->getRuleEngineState() == modsecurity::Rules::EnabledRuleEngine ? 356 | FilterHeadersStatus::StopIteration : 357 | FilterHeadersStatus::Continue; 358 | } 359 | 360 | FilterDataStatus HttpModSecurityFilter::getResponseStatus() { 361 | if (intervined_ || response_processed_) { 362 | // If intervined, let encodeData return the localReply 363 | ENVOY_LOG(debug, "Continue"); 364 | return FilterDataStatus::Continue; 365 | } 366 | // If disruptive, hold until response_processed_, otherwise let the data flow. 367 | ENVOY_LOG(debug, "RuleEngine"); 368 | return modsec_transaction_->getRuleEngineState() == modsecurity::Rules::EnabledRuleEngine ? 369 | FilterDataStatus::StopIterationAndBuffer : 370 | FilterDataStatus::Continue; 371 | 372 | } 373 | 374 | void HttpModSecurityFilter::_logCb(void *data, const void *ruleMessagev) { 375 | auto filter_ = reinterpret_cast(data); 376 | auto ruleMessage = reinterpret_cast(ruleMessagev); 377 | 378 | filter_->logCb(ruleMessage); 379 | } 380 | 381 | void HttpModSecurityFilter::logCb(const modsecurity::RuleMessage * ruleMessage) { 382 | if (ruleMessage == nullptr) { 383 | ENVOY_LOG(error, "ruleMessage == nullptr"); 384 | return; 385 | } 386 | 387 | ENVOY_LOG(info, "Rule Id: {} phase: {}", 388 | ruleMessage->m_ruleId, 389 | ruleMessage->m_phase); 390 | ENVOY_LOG(info, "* {} action. {}", 391 | // Note - since ModSecurity >= v3.0.3 disruptive actions do not invoke the callback 392 | // see https://github.com/SpiderLabs/ModSecurity/commit/91daeee9f6a61b8eda07a3f77fc64bae7c6b7c36 393 | ruleMessage->m_isDisruptive ? "Disruptive" : "Non-disruptive", 394 | modsecurity::RuleMessage::log(ruleMessage)); 395 | config_->webhook_fetcher()->invoke(getRuleMessageAsJsonString(ruleMessage)); 396 | } 397 | 398 | } // namespace Http 399 | } // namespace Envoy 400 | -------------------------------------------------------------------------------- /http-filter-modsecurity/http_filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common/common/logger.h" 6 | #include "envoy/server/filter_config.h" 7 | #include "envoy/thread_local/thread_local.h" 8 | #include "well_known_names.h" 9 | #include "webhook_fetcher.h" 10 | 11 | #include "http-filter-modsecurity/http_filter.pb.h" 12 | 13 | #include "modsecurity/modsecurity.h" 14 | #include "modsecurity/rules.h" 15 | 16 | namespace Envoy { 17 | namespace Http { 18 | 19 | class HttpModSecurityFilterConfig : public Logger::Loggable, 20 | public WebhookFetcherCallback { 21 | public: 22 | HttpModSecurityFilterConfig(const modsecurity::ModsecurityFilterConfigDecoder& proto_config, 23 | Server::Configuration::FactoryContext&); 24 | ~HttpModSecurityFilterConfig(); 25 | 26 | const std::string& rules_path() const { return rules_path_; } 27 | const std::string& rules_inline() const { return rules_inline_; } 28 | const modsecurity::ModsecurityWebhook& webhook() const { return webhook_; } 29 | 30 | WebhookFetcherSharedPtr webhook_fetcher(); 31 | 32 | std::shared_ptr modsec_; 33 | std::shared_ptr modsec_rules_; 34 | 35 | // Webhook Callbacks 36 | void onSuccess(const Http::MessagePtr& response) override; 37 | void onFailure(FailureReason reason) override; 38 | 39 | private: 40 | 41 | struct ThreadLocalWebhook : public ThreadLocal::ThreadLocalObject { 42 | ThreadLocalWebhook(WebhookFetcher* webhook_fetcher) : webhook_fetcher_(webhook_fetcher) {} 43 | WebhookFetcherSharedPtr webhook_fetcher_; 44 | }; 45 | 46 | const std::string rules_path_; 47 | const std::string rules_inline_; 48 | const modsecurity::ModsecurityWebhook webhook_; 49 | ThreadLocal::SlotPtr tls_; 50 | }; 51 | 52 | typedef std::shared_ptr HttpModSecurityFilterConfigSharedPtr; 53 | 54 | /** 55 | * Transaction flow: 56 | * 1. Disruptive? 57 | * a. StopIterationAndBuffer until finished processing request 58 | * a1. Should block? sendLocalReply 59 | * decode should return StopIteration to avoid sending data to upstream. 60 | * encode should return Continue to let local reply flow back to downstream. 61 | * a2. Request is valid 62 | * decode should return Continue to let request flow upstream. 63 | * encode should return StopIterationAndBuffer until finished processing response 64 | * a2a. Should block? goto a1. 65 | * a2b. Response is valid, return Continue 66 | * 67 | * 2. Non-disruptive - always return Continue 68 | * 69 | */ 70 | class HttpModSecurityFilter : public StreamFilter, 71 | public Logger::Loggable { 72 | public: 73 | /** 74 | * This static function will be called by modsecurity and internally invoke logCb filter's method 75 | */ 76 | static void _logCb(void* data, const void* ruleMessagev); 77 | 78 | HttpModSecurityFilter(HttpModSecurityFilterConfigSharedPtr); 79 | ~HttpModSecurityFilter(); 80 | 81 | // Http::StreamFilterBase 82 | void onDestroy() override; 83 | 84 | // Http::StreamDecoderFilter 85 | FilterHeadersStatus decodeHeaders(HeaderMap&, bool end_stream) override; 86 | FilterDataStatus decodeData(Buffer::Instance&, bool end_stream) override; 87 | FilterTrailersStatus decodeTrailers(HeaderMap&) override; 88 | void setDecoderFilterCallbacks(StreamDecoderFilterCallbacks&) override; 89 | 90 | // Http::StreamEncoderFilter 91 | FilterHeadersStatus encode100ContinueHeaders(HeaderMap& headers) override; 92 | FilterHeadersStatus encodeHeaders(HeaderMap&, bool end_stream) override; 93 | FilterDataStatus encodeData(Buffer::Instance&, bool end_stream) override; 94 | FilterTrailersStatus encodeTrailers(HeaderMap&) override; 95 | void setEncoderFilterCallbacks(StreamEncoderFilterCallbacks&) override; 96 | FilterMetadataStatus encodeMetadata(MetadataMap& metadata_map) override; 97 | 98 | private: 99 | const HttpModSecurityFilterConfigSharedPtr config_; 100 | StreamDecoderFilterCallbacks* decoder_callbacks_; 101 | StreamEncoderFilterCallbacks* encoder_callbacks_; 102 | std::shared_ptr modsec_transaction_; 103 | 104 | void logCb(const modsecurity::RuleMessage * ruleMessage); 105 | /** 106 | * @return true if intervention of current transaction is disruptive, false otherwise 107 | */ 108 | bool intervention(); 109 | 110 | FilterHeadersStatus getRequestHeadersStatus(); 111 | FilterDataStatus getRequestStatus(); 112 | 113 | FilterHeadersStatus getResponseHeadersStatus(); 114 | FilterDataStatus getResponseStatus(); 115 | 116 | // This bool is set by intervention before generating a local reply. 117 | // Once set, it means that for this http session is already intervined and any subsequent call to the filter's methods 118 | // will return ::Continue. 119 | // This is to allow the local reply to flow back to the downstream. 120 | bool intervined_; 121 | bool request_processed_; 122 | bool response_processed_; 123 | // TODO - convert three booleans to state? 124 | }; 125 | 126 | 127 | } // namespace Http 128 | } // namespace Envoy 129 | -------------------------------------------------------------------------------- /http-filter-modsecurity/http_filter.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package modsecurity; 4 | 5 | import "google/protobuf/duration.proto"; 6 | import "gogoproto/gogo.proto"; 7 | 8 | import "validate/validate.proto"; 9 | 10 | message ModsecurityFilterConfigDecoder { 11 | // If set, rules are loaded from this path 12 | string rules_path = 1; 13 | 14 | // If set, rules are loaded from this inline configuration. 15 | // Note, if both rules_path and rules_inline are set, rules_path is first loaded and afterwards rules_inline is loaded 16 | string rules_inline = 2; 17 | 18 | // If set, a webhook will be called when a rule is matched. (non-disruptive actions only since ModSecurity >= v3.0.3) 19 | ModsecurityWebhook webhook = 3; 20 | } 21 | 22 | message ModsecurityWebhook { 23 | // The http server URI to trigger the webhook. 24 | HttpUri http_uri = 1 [(validate.rules).message.required = true]; 25 | 26 | // If provided, an X-Envoy-Webhook-Signature-Value header will be added to the request with HMAC-SHA256 over the contents and `secret` as its key. 27 | string secret = 2; 28 | } 29 | 30 | // TODO - HttpUri is copied from envoy/api/envoy/api/v2/core/http_uri.proto 31 | // This should use the original http_uri.proto, however, since its visibility is not public it can't be imported 32 | // 33 | // Envoy external URI descriptor 34 | message HttpUri { 35 | // The HTTP server URI. It should be a full FQDN with protocol, host and path. 36 | // 37 | // Example: 38 | // 39 | // .. code-block:: yaml 40 | // 41 | // uri: https://www.googleapis.com/oauth2/v1/certs 42 | // 43 | string uri = 1 [(validate.rules).string.min_bytes = 1]; 44 | 45 | // Specify how `uri` is to be fetched. Today, this requires an explicit 46 | // cluster, but in the future we may support dynamic cluster creation or 47 | // inline DNS resolution. See `issue 48 | // `_. 49 | oneof http_upstream_type { 50 | option (validate.required) = true; 51 | // A cluster is created in the Envoy "cluster_manager" config 52 | // section. This field specifies the cluster name. 53 | // 54 | // Example: 55 | // 56 | // .. code-block:: yaml 57 | // 58 | // cluster: jwks_cluster 59 | // 60 | string cluster = 2 [(validate.rules).string.min_bytes = 1]; 61 | } 62 | 63 | // Sets the maximum duration in milliseconds that a response can take to arrive upon request. 64 | google.protobuf.Duration timeout = 3 [ 65 | (validate.rules).duration.gte = {}, 66 | (validate.rules).duration.required = true, 67 | (gogoproto.stdduration) = true 68 | ]; 69 | } 70 | -------------------------------------------------------------------------------- /http-filter-modsecurity/http_filter_config.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "http_filter.h" 5 | 6 | #include "common/config/json_utility.h" 7 | #include "envoy/registry/registry.h" 8 | 9 | #include "http-filter-modsecurity/http_filter.pb.h" 10 | #include "http-filter-modsecurity/http_filter.pb.validate.h" 11 | 12 | namespace Envoy { 13 | namespace Server { 14 | namespace Configuration { 15 | 16 | class HttpModSecurityFilterConfig : public NamedHttpFilterConfigFactory { 17 | public: 18 | Http::FilterFactoryCb createFilterFactory(const Json::Object& json_config, const std::string&, 19 | FactoryContext& context) override { 20 | 21 | modsecurity::ModsecurityFilterConfigDecoder proto_config; 22 | translateHttpModSecurityFilter(json_config, proto_config); 23 | 24 | return createFilter(proto_config, context); 25 | } 26 | 27 | Http::FilterFactoryCb createFilterFactoryFromProto(const Protobuf::Message& proto_config, 28 | const std::string&, 29 | FactoryContext& context) override { 30 | 31 | return createFilter( 32 | Envoy::MessageUtil::downcastAndValidate(proto_config), context); 33 | } 34 | 35 | /** 36 | * Return the Protobuf Message that represents your config incase you have config proto 37 | */ 38 | ProtobufTypes::MessagePtr createEmptyConfigProto() override { 39 | return ProtobufTypes::MessagePtr{new modsecurity::ModsecurityFilterConfigDecoder()}; 40 | } 41 | 42 | std::string name() override { 43 | return Envoy::Http::ModSecurityFilterNames::get().ModSecurity; 44 | } 45 | 46 | private: 47 | Http::FilterFactoryCb createFilter(const modsecurity::ModsecurityFilterConfigDecoder& proto_config, FactoryContext& context) { 48 | Http::HttpModSecurityFilterConfigSharedPtr config = 49 | std::make_shared(proto_config, context); 50 | 51 | return [config, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { 52 | callbacks.addStreamFilter( 53 | std::make_shared(config) 54 | ); 55 | }; 56 | } 57 | 58 | void translateHttpModSecurityFilter(const Json::Object& json_config, 59 | modsecurity::ModsecurityFilterConfigDecoder& proto_config) { 60 | 61 | // normally we want to validate the json_config againts a defined json-schema here. 62 | JSON_UTIL_SET_STRING(json_config, proto_config, rules_path); 63 | JSON_UTIL_SET_STRING(json_config, proto_config, rules_inline); 64 | } 65 | }; 66 | 67 | /** 68 | * Static registration for this sample filter. @see RegisterFactory. 69 | */ 70 | static Registry::RegisterFactory 71 | register_; 72 | 73 | } // namespace Configuration 74 | } // namespace Server 75 | } // namespace Envoy 76 | -------------------------------------------------------------------------------- /http-filter-modsecurity/http_filter_integration_test.cc: -------------------------------------------------------------------------------- 1 | #include "test/integration/http_integration.h" 2 | #include "test/integration/utility.h" 3 | 4 | namespace Envoy { 5 | class HttpFilterSampleIntegrationTest : public HttpIntegrationTest, 6 | public testing::TestWithParam { 7 | public: 8 | HttpFilterSampleIntegrationTest() 9 | : HttpIntegrationTest(Http::CodecClient::Type::HTTP1, GetParam()) {} 10 | /** 11 | * Initializer for an individual integration test. 12 | */ 13 | void SetUp() override { initialize(); } 14 | 15 | void initialize() override { 16 | config_helper_.addFilter("{ name: sample, config: { key: via, val: sample-filter } }"); 17 | HttpIntegrationTest::initialize(); 18 | } 19 | }; 20 | 21 | INSTANTIATE_TEST_SUITE_P(IpVersions, HttpFilterSampleIntegrationTest, 22 | testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); 23 | 24 | TEST_P(HttpFilterSampleIntegrationTest, Test1) { 25 | Http::TestHeaderMapImpl headers{{":method", "GET"}, {":path", "/"}, {":authority", "host"}}; 26 | 27 | IntegrationCodecClientPtr codec_client; 28 | FakeHttpConnectionPtr fake_upstream_connection; 29 | IntegrationStreamDecoderPtr response(new IntegrationStreamDecoder(*dispatcher_)); 30 | FakeStreamPtr request_stream; 31 | 32 | codec_client = makeHttpConnection(lookupPort("http")); 33 | codec_client->makeHeaderOnlyRequest(headers, *response); 34 | fake_upstream_connection = fake_upstreams_[0]->waitForHttpConnection(*dispatcher_); 35 | request_stream = fake_upstream_connection->waitForNewStream(*dispatcher_); 36 | request_stream->waitForEndStream(*dispatcher_); 37 | response->waitForEndStream(); 38 | 39 | EXPECT_STREQ("sample-filter", 40 | request_stream->headers().get(Http::LowerCaseString("via"))->value().c_str()); 41 | 42 | codec_client->close(); 43 | } 44 | } // namespace Envoy 45 | -------------------------------------------------------------------------------- /http-filter-modsecurity/utility.cc: -------------------------------------------------------------------------------- 1 | #include "utility.h" 2 | #include "modsecurity/rule_message.h" 3 | 4 | namespace Envoy { 5 | namespace Http { 6 | 7 | std::string escapeJson(const std::string& s) { 8 | std::ostringstream o; 9 | for (auto c = s.cbegin(); c != s.cend(); c++) { 10 | switch (*c) { 11 | case '"': o << "\\\""; break; 12 | case '\\': o << "\\\\"; break; 13 | case '\b': o << "\\b"; break; 14 | case '\f': o << "\\f"; break; 15 | case '\n': o << "\\n"; break; 16 | case '\r': o << "\\r"; break; 17 | case '\t': o << "\\t"; break; 18 | default: 19 | if ('\x00' <= *c && *c <= '\x1f') { 20 | o << "\\u" 21 | << std::hex << std::setw(4) << std::setfill('0') << static_cast(*c); 22 | } else { 23 | o << *c; 24 | } 25 | } 26 | } 27 | return o.str(); 28 | } 29 | 30 | // TODO - replace with a real json (rapidjson?) implementation 31 | std::string getRuleMessageAsJsonString(const modsecurity::RuleMessage* ruleMessage) { 32 | std::ostringstream ss; 33 | 34 | ss << std::boolalpha 35 | << "{" 36 | << "\"accuracy\": " << ruleMessage->m_accuracy << ", " 37 | << "\"clientIpAddress\": \"" << escapeJson(ruleMessage->m_clientIpAddress) << "\", " 38 | << "\"data\": \"" << escapeJson(ruleMessage->m_data) << "\", " 39 | << "\"id\": \"" << escapeJson(ruleMessage->m_id) << "\", " 40 | << "\"isDisruptive\": " << ruleMessage->m_isDisruptive << ", " 41 | << "\"match\": \"" << escapeJson(ruleMessage->m_match) << "\", " 42 | << "\"maturity\": " << ruleMessage->m_maturity << ", " 43 | << "\"message\": \"" << escapeJson(ruleMessage->m_message) << "\", " 44 | << "\"noAuditLog\": " << ruleMessage->m_noAuditLog << ", " 45 | << "\"phase\": " << ruleMessage->m_phase << ", " 46 | << "\"reference\": \"" << escapeJson(ruleMessage->m_reference) << "\", " 47 | << "\"rev\": \"" << escapeJson(ruleMessage->m_rev) << "\", " 48 | // Rule *m_rule; 49 | << "\"ruleFile\": \"" << escapeJson(ruleMessage->m_ruleFile) << "\", " 50 | << "\"ruleId\": " << ruleMessage->m_ruleId << ", " 51 | << "\"ruleLine\": " << ruleMessage->m_ruleLine << ", " 52 | << "\"saveMessage\": " << ruleMessage->m_saveMessage << ", " 53 | << "\"serverIpAddress\": \"" << escapeJson(ruleMessage->m_serverIpAddress) << "\", " 54 | << "\"severity\": " << ruleMessage->m_severity << ", " 55 | << "\"uriNoQueryStringDecoded\": \"" << escapeJson(ruleMessage->m_uriNoQueryStringDecoded) << "\", " 56 | << "\"ver\": \"" << escapeJson(ruleMessage->m_ver) << "\", " 57 | << "\"tags\": ["; 58 | 59 | auto begin = ruleMessage->m_tags.cbegin(); 60 | auto end = ruleMessage->m_tags.cend(); 61 | if (begin != end) { 62 | ss << "\"" << escapeJson(*begin++) << "\""; 63 | } 64 | while (begin != end) { 65 | ss << ", " 66 | << "\"" << escapeJson(*begin++) << "\""; 67 | } 68 | ss << "]" 69 | << "}"; 70 | return ss.str(); 71 | } 72 | 73 | } // namespace Http 74 | } // namespace Envoy -------------------------------------------------------------------------------- /http-filter-modsecurity/utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "modsecurity/modsecurity.h" 6 | #include "modsecurity/rules.h" 7 | 8 | namespace Envoy { 9 | namespace Http { 10 | 11 | /** 12 | * @return A json escaped string 13 | */ 14 | std::string escapeJson(const std::string& s); 15 | 16 | /** 17 | * Converts a RuleMessage to json 18 | * @return A json string 19 | */ 20 | std::string getRuleMessageAsJsonString(const modsecurity::RuleMessage* ruleMessage); 21 | 22 | } // Http 23 | } // Envoy 24 | -------------------------------------------------------------------------------- /http-filter-modsecurity/webhook_fetcher.cc: -------------------------------------------------------------------------------- 1 | #include "webhook_fetcher.h" 2 | 3 | #include "common/buffer/buffer_impl.h" 4 | #include "common/common/enum_to_int.h" 5 | #include "common/common/hex.h" 6 | #include "common/crypto/utility.h" 7 | #include "common/http/headers.h" 8 | #include "common/http/utility.h" 9 | #include "common/crypto/utility.h" 10 | 11 | namespace Envoy { 12 | namespace Http { 13 | 14 | WebhookFetcher::WebhookFetcher(Upstream::ClusterManager& cm, 15 | const modsecurity::HttpUri& uri, 16 | const std::string& secret, 17 | WebhookFetcherCallback& callback) 18 | : cm_(cm), uri_(uri), secret_(secret.cbegin(), secret.cend()), callback_(callback) {} 19 | 20 | WebhookFetcher::~WebhookFetcher() {} 21 | 22 | void WebhookFetcher::invoke(const std::string& body) { 23 | if (!cm_.get(uri_.cluster())) { 24 | ENVOY_LOG(error, "Webhook can't be invoked. cluster '{}' not found", uri_.cluster()); 25 | return; 26 | } 27 | // TODO - this conversion is unnecessary once modsecurity::HttpUri will be deprecated 28 | ::envoy::api::v2::core::HttpUri http_uri; 29 | http_uri.set_uri(uri_.uri()); 30 | http_uri.set_cluster(uri_.cluster()); 31 | http_uri.set_allocated_timeout(new ::google::protobuf::Duration(uri_.timeout())); 32 | 33 | Http::MessagePtr message = Http::Utility::prepareHeaders(http_uri); 34 | message->headers().insertMethod().value().setReference(Http::Headers::get().MethodValues.Post); 35 | message->headers().insertContentType().value().setReference(Http::Headers::get().ContentTypeValues.Json); 36 | message->headers().insertContentLength().value().setInteger(body.size()); 37 | message->body() = std::make_unique(body); 38 | if (secret_.size()) { 39 | // Add digest to headers 40 | message->headers().addCopy(WebhookHeaders::get().SignatureType, WebhookConstants::get().Sha256Hmac); 41 | message->headers().addCopy(WebhookHeaders::get().SignatureValue, Hex::encode(Envoy::Common::Crypto::Utility::getSha256Hmac(secret_, body))); 42 | } 43 | 44 | ENVOY_LOG(debug, "Webhook [uri = {}]: start", uri_.uri()); 45 | cm_.httpAsyncClientForCluster(uri_.cluster()) 46 | .send(std::move(message), *this, 47 | Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds( 48 | DurationUtil::durationToMilliseconds(uri_.timeout())))); 49 | } 50 | 51 | void WebhookFetcher::onSuccess(Http::MessagePtr&& response) { 52 | const uint64_t status_code = Http::Utility::getResponseStatus(response->headers()); 53 | if (status_code == enumToInt(Http::Code::OK)) { 54 | ENVOY_LOG(debug, "Webhook [uri = {}]: success", uri_.uri()); 55 | callback_.onSuccess(response); 56 | } else { 57 | ENVOY_LOG(debug, "Webhook [uri = {}]: bad response status code {}", uri_.uri(), 58 | status_code); 59 | callback_.onFailure(FailureReason::BadHttpStatus); 60 | } 61 | 62 | } 63 | 64 | void WebhookFetcher::onFailure(Http::AsyncClient::FailureReason reason) { 65 | ENVOY_LOG(debug, "Webhook [uri = {}]: network error {}", uri_.uri(), enumToInt(reason)); 66 | callback_.onFailure(FailureReason::Network); 67 | } 68 | 69 | } // namespace Http 70 | } // namespace Envoy -------------------------------------------------------------------------------- /http-filter-modsecurity/webhook_fetcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "envoy/common/pure.h" 6 | #include "envoy/upstream/cluster_manager.h" 7 | #include "common/common/logger.h" 8 | 9 | #include "http-filter-modsecurity/http_filter.pb.h" 10 | 11 | namespace Envoy { 12 | namespace Http { 13 | 14 | class WebhookHeaderValues { 15 | public: 16 | const Http::LowerCaseString SignatureType{"X-Envoy-Webhook-Signature-Type"}; 17 | const Http::LowerCaseString SignatureValue{"X-Envoy-Webhook-Signature-Value"}; 18 | 19 | }; 20 | 21 | using WebhookHeaders = ConstSingleton; 22 | 23 | class WebhookConstantValues { 24 | public: 25 | const std::string Sha256Hmac{"HMAC-SHA256"}; 26 | }; 27 | 28 | using WebhookConstants = ConstSingleton; 29 | 30 | /** 31 | * Failure reason. 32 | */ 33 | enum class FailureReason { 34 | /* A network error occurred causing remote data retrieval failure. */ 35 | Network, 36 | /* The webhook endpoint didn't return 200 HTTP status code */ 37 | BadHttpStatus 38 | }; 39 | 40 | /** 41 | * Callback used by webhook fetcher. 42 | */ 43 | class WebhookFetcherCallback { 44 | public: 45 | virtual ~WebhookFetcherCallback() = default; 46 | 47 | /** 48 | * This function will be called when webhook successfully called remote 49 | * @param data remote data 50 | */ 51 | virtual void onSuccess(const Http::MessagePtr& response) PURE; 52 | 53 | /** 54 | * This function is called when error happens during webhook. 55 | * @param reason failure reason. 56 | */ 57 | virtual void onFailure(FailureReason reason) PURE; 58 | }; 59 | 60 | /** 61 | * Webhook fetcher. 62 | * Currently doesn't implement any retry mechanism 63 | */ 64 | class WebhookFetcher : public Logger::Loggable, 65 | public Http::AsyncClient::Callbacks { 66 | public: 67 | WebhookFetcher(Upstream::ClusterManager& cm, 68 | const modsecurity::HttpUri& uri, 69 | const std::string& secret, 70 | WebhookFetcherCallback& callback); 71 | 72 | ~WebhookFetcher() override; 73 | 74 | // Http::AsyncClient::Callbacks 75 | void onSuccess(Http::MessagePtr&& response) override; 76 | void onFailure(Http::AsyncClient::FailureReason reason) override; 77 | 78 | /** 79 | * Calls the webhook remote URI 80 | */ 81 | void invoke(const std::string& body); 82 | 83 | private: 84 | Upstream::ClusterManager& cm_; 85 | const modsecurity::HttpUri& uri_; 86 | const std::vector secret_; 87 | WebhookFetcherCallback& callback_; 88 | }; 89 | 90 | using WebhookFetcherSharedPtr = std::shared_ptr; 91 | 92 | } // namespace Http 93 | } // namespace Envoy 94 | -------------------------------------------------------------------------------- /http-filter-modsecurity/well_known_names.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/config/well_known_names.h" 4 | 5 | namespace Envoy { 6 | namespace Http { 7 | 8 | // TODO - merge with source/extensions/filters/http/well_known_names.h 9 | /** 10 | * Well-known http filter names. 11 | */ 12 | class ModSecurityFilterNameValues { 13 | public: 14 | const std::string ModSecurity = "envoy.filters.http.modsecurity"; 15 | }; 16 | 17 | typedef ConstSingleton ModSecurityFilterNames; 18 | 19 | /** 20 | * Well-known metadata filter namespaces. 21 | */ 22 | class ModSecurityMetadataFilterValues { 23 | public: 24 | const std::string ModSecurity = "envoy.filters.http.modsecurity"; 25 | }; 26 | 27 | typedef ConstSingleton ModSecurityMetadataFilter; 28 | 29 | class MetadataModSecurityKeysValues { 30 | public: 31 | // Disable processing requests from downstream 32 | const std::string DisableRequest = "disable_request"; 33 | // Disable processing responses from upstream 34 | const std::string DisableResponse = "disable_response"; 35 | // Disable ModSecurity (both for requests and responses) 36 | const std::string Disable = "disable"; 37 | }; 38 | 39 | typedef ConstSingleton 40 | MetadataModSecurityKey; 41 | 42 | } // namespace Http 43 | } // namespace Envoy 44 | -------------------------------------------------------------------------------- /modsecurity/include: -------------------------------------------------------------------------------- 1 | ../../ModSecurity/headers -------------------------------------------------------------------------------- /modsecurity/libmodsecurity.a: -------------------------------------------------------------------------------- 1 | ../../ModSecurity/src/.libs/libmodsecurity.a --------------------------------------------------------------------------------