├── carapace-ui ├── src │ └── main │ │ └── webapp │ │ ├── .nvmrc │ │ ├── babel.config.js │ │ ├── META-INF │ │ └── context.xml │ │ ├── public │ │ ├── assets │ │ │ ├── logo.png │ │ │ └── logo-small.png │ │ └── index.html │ │ ├── vue.config.js │ │ ├── .eslintrc │ │ ├── src │ │ ├── components │ │ │ ├── StatusBox.vue │ │ │ ├── Metrics.vue │ │ │ ├── UserRealm.vue │ │ │ ├── Headers.vue │ │ │ ├── Actions.vue │ │ │ ├── Directors.vue │ │ │ ├── RequestFilters.vue │ │ │ ├── Peers.vue │ │ │ ├── Routes.vue │ │ │ ├── Listeners.vue │ │ │ ├── Navbar.vue │ │ │ └── ConnectionPools.vue │ │ ├── variables.scss │ │ ├── lib │ │ │ └── formatter.js │ │ ├── serverapi.js │ │ ├── app.scss │ │ └── main.js │ │ └── package.json └── deploy.sh ├── carapace-server ├── untilfail ├── src │ ├── test │ │ ├── resources │ │ │ ├── test-static-page.html │ │ │ ├── ca.p12 │ │ │ ├── ia.p12 │ │ │ ├── localhost.p12 │ │ │ ├── jaas │ │ │ │ └── test_jaas.conf │ │ │ ├── gencert.sh │ │ │ ├── ia.csr │ │ │ ├── localhost.csr │ │ │ ├── ia.crt │ │ │ ├── localhost.crt │ │ │ └── ca.crt │ │ └── java │ │ │ └── org │ │ │ └── carapaceproxy │ │ │ ├── core │ │ │ ├── EndpointKeyTest.java │ │ │ └── JettyHttpTraceMethodTest.java │ │ │ ├── utils │ │ │ ├── ApacheHttpUtils.java │ │ │ ├── TestUserRealm.java │ │ │ └── RawHttpServer.java │ │ │ ├── server │ │ │ ├── certificates │ │ │ │ └── Route53ClientTest.java │ │ │ └── filters │ │ │ │ └── XTlsProtocolFilterTest.java │ │ │ └── api │ │ │ └── AuthenticationAPIServerTest.java │ └── main │ │ ├── resources │ │ ├── conf │ │ │ ├── user.properties │ │ │ ├── localhost.p12 │ │ │ ├── zoo.cfg │ │ │ ├── server.properties │ │ │ └── cluster.properties │ │ ├── default-error-pages │ │ │ ├── 400_badrequest.html │ │ │ ├── 500_maintenance.html │ │ │ ├── 503_serviceunavailable.html │ │ │ ├── 404_notfound.html │ │ │ └── 500_internalservererror.html │ │ ├── bin │ │ │ ├── carapace.service │ │ │ ├── setenv.sh │ │ │ └── java-utils.sh │ │ └── logback.xml │ │ ├── java │ │ └── org │ │ │ └── carapaceproxy │ │ │ ├── configstore │ │ │ ├── ConfigurationConsumer.java │ │ │ ├── ConfigurationStoreException.java │ │ │ └── ConfigurationStoreUtils.java │ │ │ ├── server │ │ │ ├── filters │ │ │ │ ├── XTlsCipherRequestFilter.java │ │ │ │ ├── XTlsProtocolRequestFilter.java │ │ │ │ ├── BasicRequestFilter.java │ │ │ │ ├── XForwardedForRequestFilter.java │ │ │ │ ├── RegexpMapUserIdFilter.java │ │ │ │ ├── RegexpMapSessionIdFilter.java │ │ │ │ └── RequestFilterFactory.java │ │ │ ├── config │ │ │ │ ├── ConfigurationNotValidException.java │ │ │ │ ├── BackendSelector.java │ │ │ │ ├── ConfigurationChangeInProgressException.java │ │ │ │ ├── RequestFilterConfiguration.java │ │ │ │ ├── DirectorConfiguration.java │ │ │ │ ├── SafeBackendSelector.java │ │ │ │ ├── RouteConfiguration.java │ │ │ │ ├── ConnectionPoolConfiguration.java │ │ │ │ └── ActionConfiguration.java │ │ │ ├── certificates │ │ │ │ ├── DynamicCertificatesManagerException.java │ │ │ │ ├── DynamicCertificateState.java │ │ │ │ └── ocsp │ │ │ │ │ ├── Digester.java │ │ │ │ │ └── OcspRequestBuilder.java │ │ │ ├── mapper │ │ │ │ ├── requestmatcher │ │ │ │ │ ├── SecureRequestMatcher.java │ │ │ │ │ ├── RequestMatcher.java │ │ │ │ │ ├── MatchingContext.java │ │ │ │ │ ├── MatchAllRequestMatcher.java │ │ │ │ │ ├── NotRequestMatcher.java │ │ │ │ │ ├── EqualsRequestMatcher.java │ │ │ │ │ ├── AndRequestMatcher.java │ │ │ │ │ ├── OrRequestMatcher.java │ │ │ │ │ └── RegexpRequestMatcher.java │ │ │ │ ├── CustomHeader.java │ │ │ │ └── RandomBackendSelector.java │ │ │ └── cache │ │ │ │ ├── CacheImpl.java │ │ │ │ └── CacheByteBufMemoryUsageMetric.java │ │ │ ├── api │ │ │ ├── ServiceUpResource.java │ │ │ ├── response │ │ │ │ ├── SimpleResponse.java │ │ │ │ └── FormValidationResponse.java │ │ │ ├── MetricsResource.java │ │ │ ├── UserRealmResource.java │ │ │ ├── ForceHeadersAPIRequestsFilter.java │ │ │ ├── ApplicationConfig.java │ │ │ ├── DirectorsResource.java │ │ │ ├── ListenersResource.java │ │ │ ├── CacheResource.java │ │ │ └── HeadersResource.java │ │ │ ├── core │ │ │ ├── RequestFilter.java │ │ │ ├── UriCleanerHandler.java │ │ │ ├── ForwardedStrategy.java │ │ │ ├── OcspSslHandler.java │ │ │ └── EndpointKey.java │ │ │ ├── client │ │ │ ├── EndpointNotAvailableException.java │ │ │ ├── ConnectionsManagerStats.java │ │ │ ├── ConnectionsManager.java │ │ │ └── EndpointConnection.java │ │ │ ├── user │ │ │ ├── UserRealm.java │ │ │ └── SimpleUserRealm.java │ │ │ ├── EndpointStats.java │ │ │ ├── SimpleHTTPResponse.java │ │ │ ├── utils │ │ │ ├── StringUtils.java │ │ │ ├── IOUtils.java │ │ │ └── AlpnUtils.java │ │ │ └── cluster │ │ │ └── impl │ │ │ └── NullGroupMembershipHandler.java │ │ ├── stats │ │ └── prometheus.yml │ │ └── assemble │ │ └── zip-assembly.xml ├── nbactions.xml └── nb-configuration.xml ├── NOTICE ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── docker ├── build.sh ├── docker-compose.yml ├── entrypoint.sh └── Dockerfile ├── .github ├── workflows │ ├── pr-validation-report.yml │ ├── master-snapshot.yml │ └── pr-validation.yml ├── issue_template.md └── pull_request_template.md ├── licenseheader.txt ├── run.sh └── README.md /carapace-ui/src/main/webapp/.nvmrc: -------------------------------------------------------------------------------- 1 | v20.11.1 2 | -------------------------------------------------------------------------------- /carapace-server/untilfail: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | while $@; do :; done 3 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/test-static-page.html: -------------------------------------------------------------------------------- 1 | Test static page -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/META-INF/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/conf/user.properties: -------------------------------------------------------------------------------- 1 | # List of users user.username=password 2 | user.admin=admin 3 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/ca.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diennea/carapaceproxy/HEAD/carapace-server/src/test/resources/ca.p12 -------------------------------------------------------------------------------- /carapace-server/src/test/resources/ia.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diennea/carapaceproxy/HEAD/carapace-server/src/test/resources/ia.p12 -------------------------------------------------------------------------------- /carapace-server/src/main/resources/default-error-pages/400_badrequest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bad request 4 | 5 | 6 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/localhost.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diennea/carapaceproxy/HEAD/carapace-server/src/test/resources/localhost.p12 -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This software contains code regarding OCSP copied from The Netty Project https://github.com/netty/netty and Bouncy Castle https://www.bouncycastle.org/ -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/public/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diennea/carapaceproxy/HEAD/carapace-ui/src/main/webapp/public/assets/logo.png -------------------------------------------------------------------------------- /carapace-server/src/main/resources/conf/localhost.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diennea/carapaceproxy/HEAD/carapace-server/src/main/resources/conf/localhost.p12 -------------------------------------------------------------------------------- /carapace-server/src/main/resources/default-error-pages/500_maintenance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Maintenance in progress 4 | 5 | 6 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/default-error-pages/503_serviceunavailable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Service Unavailable 4 | 5 | 6 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/public/assets/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diennea/carapaceproxy/HEAD/carapace-ui/src/main/webapp/public/assets/logo-small.png -------------------------------------------------------------------------------- /carapace-server/src/main/resources/conf/zoo.cfg: -------------------------------------------------------------------------------- 1 | # Base stand-alone ZooKeeper configuration file 2 | tickTime=2000 3 | dataDir=${user.dir}/dbdata/zookeeper 4 | clientPort=2181 5 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/default-error-pages/404_notfound.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | The requested resource was not found 4 | 5 | 6 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/default-error-pages/500_internalservererror.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | An internal error occurred 4 | 5 | 6 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapperVersion=3.3.4 2 | distributionType=only-script 3 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip 4 | -------------------------------------------------------------------------------- /carapace-ui/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mvn clean install -Pproduction 3 | unzip target/*.war -d ../carapace-server/target/carapace-server*/web 4 | cd ../carapace-server/target 5 | carapace-server*/bin/service server console conf/server.properties 6 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/vue.config.js: -------------------------------------------------------------------------------- 1 | const DEV_MODE = process.env.NODE_ENV !== "production"; 2 | module.exports = { 3 | publicPath: DEV_MODE ? '' : '/ui/', 4 | configureWebpack: { 5 | resolve: { 6 | alias: { 7 | '@': 'src' 8 | } 9 | } 10 | }, 11 | } -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/configstore/ConfigurationConsumer.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.configstore; 2 | 3 | import java.util.function.Consumer; 4 | 5 | @FunctionalInterface 6 | public interface ConfigurationConsumer extends Consumer { 7 | } 8 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/jaas/test_jaas.conf: -------------------------------------------------------------------------------- 1 | Server { 2 | org.apache.zookeeper.server.auth.DigestLoginModule required 3 | user_magnews="diennea"; 4 | }; 5 | 6 | Client { 7 | org.apache.zookeeper.server.auth.DigestLoginModule required 8 | username="magnews" 9 | password="diennea"; 10 | }; -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ROOT_DIR=$(git rev-parse --show-toplevel) 5 | 6 | cp "$ROOT_DIR"/carapace-server/target/carapace-server-*.zip "$ROOT_DIR"/docker 7 | 8 | cd "$ROOT_DIR"/docker 9 | TARBALL=$(realpath carapace-server-*.zip) 10 | 11 | docker build -t carapace/carapace-server:latest --build-arg TARBALL="$(basename "$TARBALL")" . 12 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "node": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:vue/essential" 9 | ], 10 | "rules": { 11 | "vue/multi-word-component-names": "off" 12 | }, 13 | "parserOptions": { 14 | "parser": "@babel/eslint-parser" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/gencert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | openssl genrsa -out ca.key 4096 4 | openssl req -new -x509 -days 1826 -key ca.key -out ca.crt 5 | openssl genrsa -out ia.key 4096 6 | openssl req -new -key ia.key -out ia.csr 7 | openssl x509 -req -days 730 -in ia.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out ia.crt 8 | openssl pkcs12 -export -out ia.p12 -inkey ia.key -in ia.crt -chain -CAfile ca.crt 9 | openssl pkcs12 -export -out ca.p12 -inkey ca.key -in ca.crt 10 | 11 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/StatusBox.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | zookeeper: 5 | image: zookeeper:3.7 6 | container_name: zookeeper 7 | ports: 8 | - "2181:2181" 9 | carapace: 10 | image: carapace/carapace-server:latest 11 | container_name: carapace 12 | environment: 13 | CARAPACE_mode: cluster 14 | CARAPACE_zkAddress: zookeeper:2181 15 | CARAPACE_peer.id: test 16 | depends_on: 17 | - zookeeper 18 | ports: 19 | - "8001:8001" 20 | - "4089:4089" 21 | - "8089:8089" 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/pr-validation-report.yml: -------------------------------------------------------------------------------- 1 | name: PR Validation report 2 | on: 3 | workflow_run: 4 | workflows: 5 | - 'PR Validation' 6 | types: 7 | - completed 8 | permissions: 9 | contents: read 10 | actions: read 11 | checks: write 12 | jobs: 13 | report: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: dorny/test-reporter@v2 17 | with: 18 | artifact: test-results # as named in pr-validation workflow 19 | name: Maven Tests 20 | path: carapace-*/target/surefire-reports/*.xml # (inside artifact .zip) 21 | reporter: java-junit 22 | -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | CONFIGURATION="/carapace/conf/server.properties" 5 | 6 | env | while IFS='=' read -r n v; do 7 | if [[ "$n" == CARAPACE_* ]]; then 8 | property=${n/CARAPACE_/} 9 | property=${property/_/\.} 10 | escaped_value=$(printf '%s\n' "$v" | sed -e 's/[\/&]/\\&/g') 11 | grep -q "$property" "$CONFIGURATION" && sed -i "s/$property.*/$property=$escaped_value/g" "$CONFIGURATION" \ 12 | || echo -e "\n$property=$escaped_value" >> "$CONFIGURATION" 13 | printf "Setting configuration entry %s: %s\n" "$property" "$v" 14 | fi 15 | done 16 | 17 | exec "$@" 18 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/bin/carapace.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Carapace Service 3 | Documentation=https://github.com/diennea/carapaceproxy/wiki 4 | Requires=network.target 5 | After=network.target 6 | 7 | [Service] 8 | Type=forking 9 | User={RUNASUSER} 10 | Group={RUNASUSER_GROUP} 11 | PIDFile={CARAPACE_INSTALLATION_PATH}/server.java.pid 12 | ExecStart={CARAPACE_INSTALLATION_PATH}/bin/service server start 13 | ExecStop={CARAPACE_INSTALLATION_PATH}/bin/service server stop 14 | ExecReload={CARAPACE_INSTALLATION_PATH}/bin/service server restart 15 | WorkingDirectory={CARAPACE_INSTALLATION_PATH} 16 | 17 | [Install] 18 | WantedBy=default.target 19 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Metrics.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /.github/workflows/master-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Maven Snapshot 2 | on: 3 | push: 4 | branches: 5 | - master 6 | permissions: 7 | contents: write 8 | packages: write 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | - name: 'Set up JDK 21' 15 | uses: actions/setup-java@v5 16 | with: 17 | java-version: '21' 18 | distribution: 'temurin' 19 | cache: 'maven' 20 | - name: 'Build and publish snapshot' 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | run: ./mvnw -B deploy --file pom.xml -DskipTests 24 | - name: 'Submit Dependency Snapshot' 25 | uses: advanced-security/maven-dependency-submission-action@v5 26 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/conf/server.properties: -------------------------------------------------------------------------------- 1 | # example configuration with database 2 | config.type=database 3 | 4 | # API 5 | http.admin.enabled=true 6 | http.admin.port=8001 7 | http.admin.host=localhost 8 | https.admin.port=4001 9 | https.admin.sslcertfile=conf/localhost.p12 10 | https.admin.sslcertfilepassword=testproxy 11 | #admin.advertised.host=localhost #to customize the hostname shown for Admin/API/peers urls 12 | 13 | admin.accesslog.path=admin.access.log 14 | admin.accesslog.format.timezone=GMT 15 | #number of days after wich rotated log files will be deleted 16 | admin.accesslog.retention.days=90 17 | 18 | # User Realm 19 | #userrealm.class=org.carapaceproxy.user.FileUserRealm 20 | #userrealm.path=conf/user.properties 21 | 22 | # AWS Credentials 23 | #aws.accesskey= 24 | #aws.secretkey= -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/core/EndpointKeyTest.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.core; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import org.junit.Test; 6 | 7 | public class EndpointKeyTest { 8 | 9 | @Test 10 | public void endpointKeyTest() { 11 | { 12 | EndpointKey entryPoint = EndpointKey.make("localhost:8080"); 13 | assertThat(entryPoint.host(), is("localhost")); 14 | assertThat(entryPoint.port(), is(8080)); 15 | } 16 | { 17 | EndpointKey entryPoint = EndpointKey.make("localhost"); 18 | assertThat(entryPoint.host(), is("localhost")); 19 | assertThat(entryPoint.port(), is(0)); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/variables.scss: -------------------------------------------------------------------------------- 1 | // App colors 2 | $primary: #42b883; 3 | $primary-bright: #f4f5f7; 4 | $primary-dark: #35495e; 5 | $primary-dark-accent1: #4c6c8b; 6 | $primary-dark-accent2: #415b75; 7 | 8 | // Other colors 9 | $white: #f2f2f2; 10 | $info: #4277b8; 11 | $success: #42b883; 12 | $error: rgba(230, 50, 50, 0.8); 13 | $error-dark: #b84842; 14 | $warning: #fde06a; 15 | $warning-dark: #e68b179c; 16 | $shadow: rgba(65, 91, 117, 0.6); 17 | 18 | // Components colors 19 | $navbar-background: $primary; 20 | $navbar-maintenance: $warning; 21 | $navbar-logo: $primary-dark; 22 | $navbar-elements: $primary-bright; 23 | $sidebar-background: $primary-dark; 24 | $sidebar-elements: $primary-bright; 25 | 26 | // Sizing 27 | $navbar-height: 65px; 28 | $sidebar-width: 240px; 29 | $sidebar-collapsed-width: 65px; 30 | 31 | -------------------------------------------------------------------------------- /licenseheader.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ -------------------------------------------------------------------------------- /carapace-server/src/main/resources/bin/setenv.sh: -------------------------------------------------------------------------------- 1 | # Basic Environment and Java variables 2 | 3 | #JAVA_HOME= 4 | JDK_JAVA_OPTIONS="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.logging/java.util.logging=ALL-UNNAMED" 5 | JAVA_OPTS="-XX:+UseG1GC -Duser.language=en -Xmx4g -Xms4g -Djava.net.preferIPv4Stack=true -XX:MaxDirectMemorySize=4g" 6 | 7 | if [ -z "$JAVA_HOME" ]; then 8 | JAVA_PATH=`which java 2>/dev/null` 9 | if [ "x$JAVA_PATH" != "x" ]; then 10 | JAVA_BIN=`dirname $JAVA_PATH 2>/dev/null` 11 | JAVA_HOME=`dirname $JAVA_BIN 2>/dev/null` 12 | fi 13 | if [ -z "$JAVA_HOME" ]; then 14 | echo "JAVA_HOME environment variable is not defined and is needed to run this program" 15 | exit 1 16 | fi 17 | fi 18 | -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/utils/ApacheHttpUtils.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.utils; 2 | 3 | import org.apache.http.conn.ssl.NoopHostnameVerifier; 4 | import org.apache.http.impl.client.CloseableHttpClient; 5 | import org.apache.http.impl.client.HttpClients; 6 | import org.apache.http.ssl.SSLContextBuilder; 7 | 8 | public class ApacheHttpUtils { 9 | 10 | public static CloseableHttpClient createHttpClientWithDisabledSSLValidation() throws Exception { 11 | return HttpClients.custom() 12 | .setSSLContext(SSLContextBuilder.create() 13 | .loadTrustMaterial((chain, authType) -> true) // Trust all certificates 14 | .build()) 15 | .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) // Disable hostname verification 16 | .build(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Carapace 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/filters/XTlsCipherRequestFilter.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.server.filters; 2 | 3 | import org.carapaceproxy.core.ProxyRequest; 4 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 5 | 6 | public class XTlsCipherRequestFilter extends BasicRequestFilter { 7 | public static final String TYPE = "add-x-tls-cipher"; 8 | 9 | public XTlsCipherRequestFilter(RequestMatcher matcher) { 10 | super(matcher); 11 | } 12 | 13 | @Override 14 | public void apply(ProxyRequest request) { 15 | if (!checkRequestMatching(request)) { 16 | return; 17 | } 18 | if (request.isSecure()) { 19 | request.getRequestHeaders().remove("X-Tls-Cipher"); 20 | request.getRequestHeaders().add("X-Tls-Cipher", request.getCipherSuite()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/UserRealm.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | 32 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/filters/XTlsProtocolRequestFilter.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.server.filters; 2 | 3 | import org.carapaceproxy.core.ProxyRequest; 4 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 5 | 6 | public class XTlsProtocolRequestFilter extends BasicRequestFilter { 7 | 8 | public static final String TYPE = "add-x-tls-protocol"; 9 | 10 | public XTlsProtocolRequestFilter(RequestMatcher matcher) { 11 | super(matcher); 12 | } 13 | 14 | @Override 15 | public void apply(ProxyRequest request) { 16 | if (!checkRequestMatching(request)) { 17 | return; 18 | } 19 | if (request.isSecure()) { 20 | request.getRequestHeaders().remove("X-Tls-Protocol"); 21 | request.getRequestHeaders().add("X-Tls-Protocol", request.getSslProtocol()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | Is this a question, a feature request or a bug report? 2 | 3 | 4 | **FEATURE REQUEST** 5 | 6 | 1. Please describe the feature you are requesting. 7 | 8 | 2. Indicate the importance of this issue to you (blocker, must-have, should-have, nice-to-have). Are you currently using any workarounds to address this issue? 9 | 10 | 3. Provide any additional detail on your proposed use case for this feature. 11 | 12 | 4. If there are some sub-tasks using -[] for each subtask and create a corresponding issue to map to the sub task: 13 | - [ ] [sub-task1-issue-number](example_sub_issue1_link_here): sub-task1 discription here, 14 | - [ ] [sub-task2-issue-number](example_sub_issue2_link_here): sub-task2 discription here, 15 | - ... 16 | 17 | 18 | **BUG REPORT** 19 | 20 | 1. Please describe the issue you observed: 21 | 22 | - What did you do? 23 | 24 | - What did you expect to see? 25 | 26 | - What did you see instead? 27 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Following this checklist to help us incorporate your 2 | contribution quickly and easily: 3 | 4 | - [ ] Each commit in the pull request should have a meaningful subject line and body. 5 | - [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. 6 | - [ ] Remember to add the correct license header to new files. 7 | - [ ] Run `mvn clean verify` to make sure basic checks pass. A more thorough check will 8 | be performed on your pull request automatically. 9 | 10 | To make clear that you license your contribution under 11 | the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) 12 | you have to acknowledge this by using the following check-box. 13 | 14 | - [ ] I hereby declare this contribution to be licenced under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) 15 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | ARG TARBALL 4 | RUN test -n "$TARBALL" 5 | 6 | ENV ADMIN_PORT=8001 7 | ENV LISTENER_HTTP_PORT=8089 8 | ENV LISTENER_HTTPS_PORT=4089 9 | ENV CARAPACE_http.admin.host=0.0.0.0 10 | 11 | EXPOSE $ADMIN_PORT 12 | EXPOSE $LISTENER_HTTP_PORT 13 | EXPOSE $LISTENER_HTTPS_PORT 14 | 15 | RUN apt-get update \ 16 | && apt-get -y dist-upgrade \ 17 | && apt-get -y install --no-install-recommends openjdk-17-jdk-headless netcat dnsutils less curl unzip \ 18 | && apt-get -y --purge autoremove \ 19 | && apt-get autoclean \ 20 | && apt-get clean \ 21 | && rm -rf /var/lib/apt/lists/* 22 | 23 | 24 | ADD ${TARBALL} / 25 | RUN mkdir /carapace 26 | RUN unzip /carapace-server*.zip && cd /carapace-server*/ && mv ./* /carapace && rm -rf /carapace-server-* 27 | 28 | COPY entrypoint.sh /carapace/entrypoint.sh 29 | 30 | WORKDIR /carapace 31 | ENTRYPOINT ["/carapace/entrypoint.sh"] 32 | CMD ["/carapace/bin/service", "server", "console"] -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/core/JettyHttpTraceMethodTest.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.core; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import io.netty.handler.codec.http.HttpMethod; 5 | import java.net.HttpURLConnection; 6 | import java.net.URI; 7 | import java.net.URL; 8 | import javax.servlet.http.HttpServletResponse; 9 | import org.carapaceproxy.api.UseAdminServer; 10 | import org.junit.Test; 11 | 12 | public class JettyHttpTraceMethodTest extends UseAdminServer { 13 | 14 | @Test 15 | public void httpTraceMethodTest() throws Exception { 16 | startAdmin(); 17 | String URL = "http://localhost:8761"; 18 | URL url = URI.create(URL).toURL(); 19 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 20 | conn.setRequestMethod(String.valueOf(HttpMethod.TRACE)); 21 | int code = conn.getResponseCode(); 22 | assertEquals(HttpServletResponse.SC_FORBIDDEN, code); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Headers.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 34 | 35 | -------------------------------------------------------------------------------- /carapace-server/src/main/stats/prometheus.yml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 4 | evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # Alertmanager configuration 8 | alerting: 9 | alertmanagers: 10 | - static_configs: 11 | - targets: 12 | # - alertmanager:9093 13 | 14 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 15 | rule_files: 16 | # - "first_rules.yml" 17 | # - "second_rules.yml" 18 | 19 | # A scrape configuration containing exactly one endpoint to scrape: 20 | # Here it's Prometheus itself. 21 | scrape_configs: 22 | 23 | - job_name: 'proxy' 24 | scrape_interval: 15s 25 | tls_config: 26 | insecure_skip_verify: true 27 | scheme: 'http' 28 | metrics_path: '/metrics' 29 | static_configs: 30 | - targets: ['localhost:8000'] 31 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/lib/formatter.js: -------------------------------------------------------------------------------- 1 | import moment from "moment"; 2 | 3 | export const PATTERN = "YYYY/MM/DD HH:mm"; 4 | 5 | export function formatTimestamp(v) { 6 | if (!v) { 7 | return ""; 8 | } 9 | if (!isNaN(v)) { 10 | return moment(v).format(PATTERN); 11 | } 12 | return "ts:" + v; 13 | } 14 | 15 | export function parseTimestamp(v) { 16 | if (!v) { 17 | return 0; 18 | } 19 | 20 | return moment(v, PATTERN).valueOf(); 21 | } 22 | 23 | export function compareTimestamp(a, b) { 24 | if (!a && !b) { 25 | return 0; 26 | } 27 | if (!a) { 28 | return -1; 29 | } 30 | if (!b) { 31 | return 1; 32 | } 33 | var _a = moment(a, PATTERN); 34 | var _b = moment(b, PATTERN); 35 | return _a > _b ? 1 : _a < _b ? -1 : 0; 36 | } 37 | export function toInputDateValue(v) { 38 | if (!v) return ""; 39 | return new Date(v).toISOString().substr(0, 10); 40 | } 41 | 42 | export function toBooleanSymbol(value) { 43 | return value ? "Yes" : "No"; 44 | } 45 | -------------------------------------------------------------------------------- /carapace-server/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CUSTOM-dependency:tree 5 | dependency:tree 6 | 7 | clean 8 | install 9 | dependency:tree 10 | 11 | 12 | 13 | CUSTOM-compile spotbugs:check 14 | compile spotbugs:check 15 | 16 | compile 17 | spotbugs:check 18 | 19 | 20 | 21 | CUSTOM-clean generate-sources 22 | clean generate-sources 23 | 24 | clean 25 | generate-sources 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/serverapi.js: -------------------------------------------------------------------------------- 1 | export {doGet, doPost, doRequest}; 2 | 3 | function doGet(url, okCallback, failCallback, options) { 4 | options = options || {}; 5 | options.method = "GET"; 6 | doRequest(url, { options }, okCallback, failCallback); 7 | } 8 | 9 | function doPost(url, data, okCallback, failCallback, options) { 10 | options = options || {}; 11 | options.method = "POST"; 12 | if (typeof data === 'object') { 13 | options.headers = { 14 | 'Content-Type': 'application/json' 15 | } 16 | options.body = JSON.stringify(data) 17 | } else { 18 | options.body = data; 19 | } 20 | doRequest(url, { options }, okCallback, failCallback); 21 | } 22 | 23 | function doRequest(url, opt, okCallback, failCallback) { 24 | fetch(url, opt.options || {}).then(r => { 25 | const contentType = r.headers.get("content-type") 26 | const data = contentType?.includes("application/json") ? r.json() : r.text() 27 | data.then(r.ok ? okCallback : failCallback) 28 | }).catch(failCallback) 29 | } 30 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Actions.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 35 | 36 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Directors.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 39 | 40 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/configstore/ConfigurationStoreException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.configstore; 21 | 22 | /** 23 | * 24 | * @author paolo.venturi 25 | */ 26 | public class ConfigurationStoreException extends RuntimeException { 27 | 28 | public ConfigurationStoreException(Throwable cause) { 29 | super(cause); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/ServiceUpResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import javax.ws.rs.GET; 23 | import javax.ws.rs.Path; 24 | 25 | /** 26 | * Access to proxy cache 27 | * 28 | * @author enrico.olivelli 29 | */ 30 | @Path("/up") 31 | public class ServiceUpResource { 32 | 33 | @GET 34 | public String ok() { 35 | return "ok"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/core/RequestFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.core; 21 | 22 | /** 23 | * Modify a request, for instance a filter can add/drop headers. 24 | */ 25 | public interface RequestFilter { 26 | 27 | /** 28 | * Apply modifications to the given request. 29 | * 30 | * @param request 31 | */ 32 | void apply(ProxyRequest request); 33 | } 34 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/response/SimpleResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Diennea S.r.l. - Copyright 2022, all rights reserved 3 | */ 4 | package org.carapaceproxy.api.response; 5 | 6 | import io.netty.handler.codec.http.HttpResponseStatus; 7 | import javax.ws.rs.core.Response; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * Simple REST response 13 | * 14 | * @author paolo.venturi 15 | */ 16 | @Getter 17 | @NoArgsConstructor 18 | public class SimpleResponse { 19 | 20 | private String message; 21 | 22 | protected SimpleResponse(String message) { 23 | this.message = message; 24 | } 25 | 26 | public static Response ok() { 27 | return Response.status(Response.Status.OK).build(); 28 | } 29 | 30 | public static Response created() { 31 | return Response.status(Response.Status.CREATED).build(); 32 | } 33 | 34 | public static Response error(Throwable error) { 35 | while (error.getCause() != null) { 36 | error = error.getCause(); 37 | } 38 | return Response.status(HttpResponseStatus.UNPROCESSABLE_ENTITY.code()) 39 | .entity(new SimpleResponse(error.getMessage())) 40 | .build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /carapace-server/nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | ${project.basedir}/../licenseheader.txt 17 | none 18 | JDK_17 19 | 20 | 21 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/ConfigurationNotValidException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | /** 23 | * Configuration is not valid 24 | */ 25 | public class ConfigurationNotValidException extends Exception { 26 | 27 | public ConfigurationNotValidException(String message) { 28 | super(message); 29 | } 30 | 31 | public ConfigurationNotValidException(Throwable cause) { 32 | super(cause); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/RequestFilters.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/conf/cluster.properties: -------------------------------------------------------------------------------- 1 | # example cluster configuration 2 | config.type=database 3 | mode=cluster 4 | 5 | # connection string to ZooKeeper 6 | zkAddress=localhost:2181 7 | 8 | # identity of this node, it must be unique, it default to local hostname 9 | #peer.id=myserver 10 | 11 | # HerdDB embedded server configuration 12 | # Use different ports in case of running a test cluster on a single machine 13 | # db.bookie.allowLoopback=true 14 | #db.server.port=7000 15 | #db.server.bookkeeper.port=3181 16 | #db.server.bookkeeper.ensemble.size=1 17 | #db.server.bookkeeper.write.quorum.size=1 18 | #db.server.bookkeeper.ack.quorum.size=1 19 | 20 | # this is where the DB will be store (HerdDB + WAL on BookKeeper) 21 | db.server.base.dir=dbdata 22 | 23 | # API 24 | http.admin.enabled=true 25 | http.admin.port=8001 26 | http.admin.host=localhost 27 | https.admin.port=4001 28 | https.admin.sslcertfile=conf/localhost.p12 29 | https.admin.sslcertfilepassword=testproxy 30 | #admin.advertised.host=localhost #to customize the hostname shown for Admin/API/peers urls 31 | 32 | admin.accesslog.path=admin.access.log 33 | admin.accesslog.format.timezone=GMT 34 | #number of days after wich rotated log files will be deleted 35 | admin.accesslog.retention.days=90 36 | 37 | # AWS Credentials 38 | #aws.accesskey= 39 | #aws.secretkey= -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/client/EndpointNotAvailableException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.client; 21 | 22 | /** 23 | * The request endpoint is currently not available 24 | * 25 | * @author enrico.olivelli 26 | */ 27 | public class EndpointNotAvailableException extends Exception { 28 | 29 | public EndpointNotAvailableException(Throwable cause) { 30 | super(cause); 31 | } 32 | 33 | public EndpointNotAvailableException(String message, Throwable cause) { 34 | super(message, cause); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/certificates/DynamicCertificatesManagerException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.certificates; 21 | 22 | /** 23 | * 24 | * @author paolo.venturi 25 | */ 26 | public final class DynamicCertificatesManagerException extends RuntimeException { 27 | 28 | public DynamicCertificatesManagerException(String message, Throwable cause) { 29 | super(message, cause); 30 | } 31 | 32 | public DynamicCertificatesManagerException(String message) { 33 | super(message); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/client/ConnectionsManagerStats.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.client; 21 | 22 | import java.util.Map; 23 | import org.carapaceproxy.EndpointStats; 24 | import org.carapaceproxy.core.EndpointKey; 25 | 26 | /** 27 | * Stats 28 | * 29 | * @author enrico.olivelli 30 | */ 31 | public interface ConnectionsManagerStats { 32 | 33 | public Map getEndpoints(); 34 | 35 | public default EndpointStats getEndpointStats(EndpointKey key) { 36 | return getEndpoints().get(key); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/SecureRequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | /** 23 | * 24 | * Matcher for Secure request (HTTPS) 25 | * 26 | * @author paolo.venturi 27 | */ 28 | public class SecureRequestMatcher implements RequestMatcher { 29 | 30 | @Override 31 | public boolean matches(MatchingContext context) { 32 | return context.isSecure(); 33 | } 34 | 35 | @Override 36 | public String getDescription() { 37 | return "secure request"; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/user/UserRealm.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.user; 21 | 22 | import java.util.Collection; 23 | import org.carapaceproxy.configstore.ConfigurationStore; 24 | import org.carapaceproxy.server.config.ConfigurationNotValidException; 25 | 26 | /** 27 | * 28 | * @author matteo.minardi 29 | */ 30 | public interface UserRealm { 31 | 32 | public void configure(ConfigurationStore properties) throws ConfigurationNotValidException; 33 | 34 | public Collection listUsers(); 35 | 36 | public String login(String username, String password); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/RequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | /** 23 | * Generic criteria to apply a route to a request 24 | */ 25 | public interface RequestMatcher { 26 | 27 | /** 28 | * Checks request appliance to defined criteria. 29 | * @param context over the request appliance is checked. 30 | * @return 31 | */ 32 | boolean matches(MatchingContext context); 33 | 34 | /** 35 | * @return description of the matcher (used by UI). 36 | */ 37 | String getDescription(); 38 | } 39 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/MatchingContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | /** 23 | * Context used to check matching conditions over a request. 24 | * 25 | * @author paolo.venturi 26 | */ 27 | public interface MatchingContext { 28 | 29 | /** 30 | * 31 | * @param name it's expected to be lowercase. 32 | * @return property value or empty string whether not exists. 33 | */ 34 | String getProperty(String name); 35 | 36 | /** 37 | * 38 | * @return true whether HTTPS is used. 39 | */ 40 | boolean isSecure(); 41 | } 42 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/MatchAllRequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | /** 23 | * Matcher to all request 24 | * 25 | * @author paolo.venturi 26 | */ 27 | public class MatchAllRequestMatcher implements RequestMatcher { 28 | 29 | @Override 30 | public boolean matches(MatchingContext context) { 31 | return true; 32 | } 33 | 34 | @Override 35 | public String getDescription() { 36 | return "all requests"; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "MatchAll"; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/BackendSelector.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | import java.util.List; 23 | import org.carapaceproxy.server.mapper.EndpointMapper; 24 | 25 | /** 26 | * Chooses the backend which can serve the request 27 | */ 28 | public interface BackendSelector { 29 | 30 | List selectBackends(String userId, String sessionId, String director); 31 | 32 | /** 33 | * Functional interface that models a factory for selectors given the mapper they should be applied to. 34 | */ 35 | @FunctionalInterface 36 | interface SelectorFactory { 37 | 38 | BackendSelector build(EndpointMapper endpointMapper); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/ConfigurationChangeInProgressException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | /** 23 | * Cannot change configuration concurrently with another configuration change 24 | */ 25 | public class ConfigurationChangeInProgressException extends Exception { 26 | 27 | public ConfigurationChangeInProgressException(String message) { 28 | super(message); 29 | } 30 | 31 | public ConfigurationChangeInProgressException(Throwable cause) { 32 | super(cause); 33 | } 34 | 35 | public ConfigurationChangeInProgressException() { 36 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/filters/BasicRequestFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.filters; 21 | 22 | import org.carapaceproxy.core.ProxyRequest; 23 | import org.carapaceproxy.core.RequestFilter; 24 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 25 | 26 | /** 27 | * 28 | * Root class for all RequestFilters 29 | * 30 | * @author paolo.venturi 31 | */ 32 | public abstract class BasicRequestFilter implements RequestFilter { 33 | 34 | private final RequestMatcher matcher; 35 | 36 | public BasicRequestFilter(RequestMatcher matcher) { 37 | this.matcher = matcher; 38 | } 39 | 40 | boolean checkRequestMatching(ProxyRequest request) { 41 | return matcher.matches(request); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/NotRequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | /** 23 | * Matcher for composing NOT expressions with another matcher. 24 | * 25 | * @author paolo.venturi 26 | */ 27 | public class NotRequestMatcher implements RequestMatcher { 28 | 29 | private final RequestMatcher matcher; 30 | 31 | public NotRequestMatcher(RequestMatcher matcher) { 32 | this.matcher = matcher; 33 | } 34 | 35 | @Override 36 | public boolean matches(MatchingContext context) { 37 | return !matcher.matches(context); 38 | } 39 | 40 | @Override 41 | public String getDescription() { 42 | return "not " + matcher.getDescription(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/RequestFilterConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | import java.util.Map; 23 | 24 | /** 25 | * Configuration for a TLS Certificate 26 | * 27 | * @author enrico.olivelli 28 | */ 29 | public class RequestFilterConfiguration { 30 | 31 | private final String type; 32 | private final Map filterConfig; 33 | 34 | public RequestFilterConfiguration(String type, Map filterConfig) { 35 | this.type = type; 36 | this.filterConfig = filterConfig; 37 | } 38 | 39 | public String getType() { 40 | return type; 41 | } 42 | 43 | public Map getFilterConfig() { 44 | return filterConfig; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/MetricsResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import javax.servlet.ServletContext; 23 | import javax.ws.rs.GET; 24 | import javax.ws.rs.Path; 25 | import javax.ws.rs.Produces; 26 | import org.carapaceproxy.core.HttpProxyServer; 27 | 28 | /** 29 | * Access the metrics API 30 | * 31 | * @author paolo.venturi 32 | */ 33 | @Path("/metrics") 34 | @Produces("application/json") 35 | public class MetricsResource { 36 | 37 | @javax.ws.rs.core.Context 38 | ServletContext context; 39 | 40 | @GET 41 | @Path("url") 42 | @Produces("text/plain") 43 | public String getPath() { 44 | HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 45 | return server.getMetricsUrl(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/CustomHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper; 21 | 22 | import lombok.Data; 23 | 24 | /** 25 | * 26 | * Custom Header to add/set/remove in HttpResponses 27 | * 28 | * @author paolo.venturi 29 | */ 30 | @Data 31 | public final class CustomHeader { 32 | 33 | public static enum HeaderMode { 34 | ADD, // to append the header 35 | SET, // to set the header as the only one 36 | REMOVE; // to remove the header 37 | 38 | @Override 39 | public String toString() { 40 | return super.toString().toLowerCase(); 41 | } 42 | } 43 | 44 | private final String id; 45 | private final String name; 46 | private final String value; 47 | private final HeaderMode mode; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/EqualsRequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | /** 23 | * Matcher for composing EQUALS expressions give a property-key and the expected value. 24 | * 25 | * @author paolo.venturi 26 | */ 27 | public class EqualsRequestMatcher implements RequestMatcher { 28 | 29 | private final String name; 30 | private final String value; 31 | 32 | public EqualsRequestMatcher(String key, String value) { 33 | this.name = key.toLowerCase(); 34 | this.value = value; 35 | } 36 | 37 | @Override 38 | public boolean matches(MatchingContext context) { 39 | return context.getProperty(name).equals(value); 40 | } 41 | 42 | public String getDescription() { 43 | return name + " = " + value; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/server/certificates/Route53ClientTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Diennea S.r.l. under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. Diennea S.r.l. licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | package org.carapaceproxy.server.certificates; 21 | 22 | /** 23 | * Class for testing real AWS Route53 calls. 24 | * 25 | * @author paolo.venturi 26 | */ 27 | public class Route53ClientTest { 28 | 29 | //@Test 30 | public void testCRUD() throws InterruptedException { 31 | Route53Client r53Client = new Route53Client( 32 | null, null 33 | ); 34 | 35 | System.out.println("RES: " + r53Client.createDnsChallengeForDomain("*.testcara.tld", "digest")); 36 | 37 | System.out.println("RES: " + r53Client.isDnsChallengeForDomainAvailable("*.testcara.tld", "digest")); 38 | 39 | System.out.println("RES: " + r53Client.deleteDnsChallengeForDomain("*.testcara.tld", "digest")); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carapace-ui", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build && rm -rf ui && mv dist ui", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@fortawesome/fontawesome-svg-core": "6.1.1", 12 | "@fortawesome/free-regular-svg-icons": "6.1.1", 13 | "@fortawesome/free-solid-svg-icons": "6.1.1", 14 | "@fortawesome/vue-fontawesome": "2.0.6", 15 | "ansi-regex": "6.0.1", 16 | "bootstrap": "4.5.3", 17 | "bootstrap-vue": "^2.21.2", 18 | "jquery": "^3.6.0", 19 | "moment": "2.29.4", 20 | "popper.js": "^1.16.1", 21 | "serialize-javascript": "6.0.2", 22 | "vue": "^2.6.14", 23 | "vue-router": "3.5.3" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.12.16", 27 | "@babel/eslint-parser": "^7.12.16", 28 | "@vue/cli-plugin-babel": "~5.0.0", 29 | "@vue/cli-plugin-eslint": "~5.0.0", 30 | "@vue/cli-service": "~5.0.0", 31 | "eslint": "^8.0.0", 32 | "eslint-plugin-vue": "^9.27.0", 33 | "sass": "^1.77.8", 34 | "sass-loader": "^16.0.1", 35 | "vue-template-compiler": "^2.6.14", 36 | "webpack": "^5.94.0", 37 | "yarn-audit-fix": "^10.0.9", 38 | "yarn-deduplicate": "^6.0.2" 39 | }, 40 | "postcss": { 41 | "plugins": { 42 | "autoprefixer": {} 43 | } 44 | }, 45 | "browserslist": [ 46 | "> 1%", 47 | "last 2 versions", 48 | "not ie <= 8" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Licensed to Diennea S.r.l. under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. Diennea S.r.l. licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | 20 | for service in carapace-server/target/carapace-server-*/bin/service; 21 | do 22 | if [ -f "$service" ]; then 23 | # stop if running 24 | $service server stop 25 | fi 26 | done 27 | 28 | # Carapace version 29 | CARAPACE_V=$(mvn org.apache.maven.plugins:maven-help-plugin:3.1.1:evaluate -Dexpression=project.version -q -DforceStdout -Dmaven.wagon.http.ssl.insecure=true) 30 | 31 | mvn clean install -DskipTests -Pproduction 32 | cd carapace-server/target || exit 33 | unzip ./*.zip 34 | cd "carapace-server-${CARAPACE_V}" || exit 35 | ./bin/service server start 36 | 37 | timeout 22 sh -c "until nc -z \$0 \$1; do sleep 1; done" localhost 8001 38 | 39 | # apply dynamic configuration 40 | curl -X POST --data-binary @conf/server.dynamic.properties http://localhost:8001/api/config/apply --user admin:admin -H "Content-Type: text/plain" 41 | 42 | tail -f server.service.log 43 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/response/FormValidationResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Diennea S.r.l. - Copyright 2022, all rights reserved 3 | */ 4 | package org.carapaceproxy.api.response; 5 | 6 | import io.netty.handler.codec.http.HttpResponseStatus; 7 | import javax.ws.rs.core.Response; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * Form validation REST response 13 | * 14 | * @author paolo.venturi 15 | */ 16 | @Getter 17 | @NoArgsConstructor 18 | public class FormValidationResponse extends SimpleResponse { 19 | 20 | public static final String ERROR_FIELD_REQUIRED = "Value required"; 21 | public static final String ERROR_FIELD_INVALID = "Value invalid"; 22 | public static final String ERROR_FIELD_DUPLICATED = "Value already used"; 23 | 24 | private String field; 25 | 26 | private FormValidationResponse(String field, String message) { 27 | super (message); 28 | this.field = field; 29 | } 30 | 31 | public static Response fieldRequired(String field) { 32 | return fieldError(field, ERROR_FIELD_REQUIRED); 33 | } 34 | 35 | public static Response fieldInvalid(String field) { 36 | return fieldError(field, ERROR_FIELD_INVALID); 37 | } 38 | 39 | public static Response fieldError(String field, String message) { 40 | return Response.status(HttpResponseStatus.UNPROCESSABLE_ENTITY.code()) 41 | .entity(new FormValidationResponse(field, message)) 42 | .build(); 43 | } 44 | 45 | public static Response fieldConflict(String field) { 46 | return Response.status(Response.Status.CONFLICT) 47 | .entity(new FormValidationResponse(field, ERROR_FIELD_DUPLICATED)) 48 | .build(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Carapaceproxy 2 | A Distributed Java Reverse Proxy 3 | 4 | ## For Developers 5 | Start Carapace by running `./run.sh` script. This just launch the server, the ui and load a ready to use dynamic configuration. 6 | Server will start at `http://localhost:8001/ui/#/` and `https://localhost:4001/ui/#/` with no authentication required. 7 | 8 | To launch/update the ui only, run `./carapace-ui/deploy.sh` 9 | 10 | To bundle the project into a .zip archive for a standalone installation run `mvn clean install -DskipTests -Pproduction`. You'll find the generated zip in `carapace-server/target/carapace-server-X.Y.Z-SNAPSHOT.zip` 11 | 12 | ## For Admins 13 | To install Carapace, just unzip the carapace.zip archive and then run `./bin/service server start [custom-server.properties]` (default server.properties is loaded). 14 | The server will start at `hostname:port` as defined in the server.properties file loaded. 15 | 16 | ## Docker 17 | 18 | You can build carapace docker image by running: 19 | ``` 20 | mvn clean install -DskipTests -Pproduction 21 | docker/build.sh 22 | ``` 23 | Then you can run the container and the admin interface will be listening on 0.0.0.0:8001 24 | ``` 25 | docker run -p 8001:8001 carapace/carapace-server:latest 26 | ``` 27 | You can also pass system properties using docker option `-e` with the prefix `CARAPACE`: 28 | 29 | ``` 30 | docker run -p 8001:8001 -e CARAPACE_mode=cluster -e CARAPACE_zkAddress=localhost:2181 carapace/carapace-server:latest 31 | ``` 32 | 33 | ### Docker start a simple cluster with 1 node 34 | 35 | You can run a simple 1-node cluster using the docker-compose.yml file. 36 | This starts a Carapace node (with embedded bookkeeper and herddb) and Zookeeper. 37 | The replication factor of bookkeeper in this case will be set to 1. 38 | 39 | ``` 40 | docker compose up -d 41 | ``` -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/core/UriCleanerHandler.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.core; 2 | 3 | import static reactor.netty.NettyPipeline.H2OrHttp11Codec; 4 | import static reactor.netty.NettyPipeline.HttpTrafficHandler; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.ChannelInboundHandlerAdapter; 8 | import io.netty.handler.codec.http.HttpRequest; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | public class UriCleanerHandler extends ChannelInboundHandlerAdapter { 13 | public static final UriCleanerHandler INSTANCE = new UriCleanerHandler(); 14 | private final Logger logger = LoggerFactory.getLogger(UriCleanerHandler.class); 15 | 16 | private UriCleanerHandler() { 17 | super(); 18 | } 19 | 20 | public void addToPipeline(final Channel channel) { 21 | if (channel.pipeline().get(HttpTrafficHandler) != null) { 22 | channel.pipeline().addBefore(HttpTrafficHandler, "uriEncoder", this); 23 | } 24 | if (channel.pipeline().get(H2OrHttp11Codec) != null) { 25 | channel.pipeline().addAfter(H2OrHttp11Codec, "uriEncoder", this); 26 | } 27 | logger.debug("Unsupported pipeline structure: {}; skipping...", channel.pipeline().toString()); 28 | } 29 | 30 | @Override 31 | public void channelRead(final ChannelHandlerContext ctx, final Object msg) { 32 | if (msg instanceof final HttpRequest request) { 33 | request.setUri(request.uri() 34 | .replaceAll("\\[", "%5B") 35 | .replaceAll("]", "%5D") 36 | ); 37 | } 38 | ctx.fireChannelRead(msg); 39 | } 40 | 41 | @Override 42 | public boolean isSharable() { 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/app.scss: -------------------------------------------------------------------------------- 1 | @import "./variables.scss"; 2 | 3 | /* Bootstrap colors overriding */ 4 | $theme-colors: ( 5 | primary: $primary, 6 | secondary: $primary-dark 7 | ); 8 | 9 | @import "./../node_modules/bootstrap/scss/bootstrap"; 10 | 11 | /* Custom Scrollbar*/ 12 | ::-webkit-scrollbar { 13 | height: 0.5rem; 14 | width: 0.25rem; 15 | background-color: $white; 16 | } 17 | 18 | ::-webkit-scrollbar-track { 19 | box-shadow: inset 0 0 6px $shadow; 20 | background-color: $white; 21 | } 22 | 23 | ::-webkit-scrollbar-thumb { 24 | box-shadow: inset 0 0 6px $shadow; 25 | background-color: $primary; 26 | } 27 | 28 | /* Other Styles */ 29 | .status-box { 30 | display: inline-block; 31 | padding: 0.5rem; 32 | margin: 0.3em auto; 33 | border-radius: 3px; 34 | font-size: 0.95rem; 35 | 36 | &.status-box_warning { 37 | background-color: $warning; 38 | border-left: 5px solid $warning-dark; 39 | } 40 | 41 | &.status-box_error { 42 | background-color: $error; 43 | border-left: 5px solid $error-dark; 44 | } 45 | } 46 | 47 | * { 48 | font-family: "Exo 2", sans-serif; 49 | -webkit-font-smoothing: antialiased; 50 | -moz-osx-font-smoothing: grayscale; 51 | } 52 | 53 | 54 | .badge-status { 55 | font-size: 13px; 56 | border-radius: 2px; 57 | text-transform: uppercase; 58 | font-weight: bold; 59 | text-align: center; 60 | padding: 0.5rem; 61 | 62 | &.success { 63 | color: $white; 64 | background-color: $success; 65 | } 66 | 67 | &.warning { 68 | background-color: $warning; 69 | } 70 | 71 | &.error { 72 | color: $white; 73 | background-color: $error; 74 | } 75 | 76 | &.info { 77 | color: $white; 78 | background-color: $info; 79 | } 80 | } -------------------------------------------------------------------------------- /carapace-server/src/test/resources/ia.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIEvzCCAqcCAQAwYTELMAkGA1UEBhMCWFgxDjAMBgNVBAgMBUl0YWx5MQ4wDAYD 3 | VQQHDAVJdGFseTEOMAwGA1UECgwFSXRhbHkxDjAMBgNVBAsMBUl0YWx5MRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6 5 | dAMJd4NkQPkgJ4JH5ayutlgbcRTKoL9H2FDrdXEyBQNVtvVDSzQRKcThD9n3nBrl 6 | yHKQKZBjlDR6W0KXITbe5Zvu419QKVGFWXH2lF9/2Uwa7x+GZybEwTfONoqNXiPY 7 | vn1dJx9ZA1PzzAD+LXJyn8y2sp5G+iDksgPg8DxQwDi+l7ZpIz1w9TT+lsynhp5n 8 | 3z0Gjmg4rGH9+M88HTM9+dPI9MBklnoNc857COck76w6+GhWHH1mZTwClyjMg4px 9 | pNDeO/vpAxW9SbtRv5r1wTD1DB4Tn6FXw1V1Hao+n7pVPaw/X1eqazE7xHWyiSxY 10 | +NhRzHMgcW92jIIGd8weVET1QLjrJcIsRjEOL45/iSt8pspKrR/efJVXzUrFCHch 11 | qfh6PB5FZqlHEf4EmDe2cpAXgV1xBMjKs6DVZcd37EeLfdeoGm0Mloae1uDkQwID 12 | VyiR1AUA2wc6QjVAefzKl2FyZopW0Ss6zGx6M2Nhh/iBhC/BSkXP3KGzG5mQHWzK 13 | YrCpDQMkfnEPuJlXgxWbpqlzwg7v4zqp0VWZu4WBbhfo42m4OyDha42n1fpoig5/ 14 | UlNMqcPyjXuPXqmidrSGPSOnBPWhU5IZnrPui69h/oHoBy6dWKI7qTCm/jooCINL 15 | 6AvFmANTzKBNJuRvQX6Jg7ohSTPCnDH/d9BcDU1+RwIDAQABoBkwFwYJKoZIhvcN 16 | AQkHMQoMCGNoYW5nZWl0MA0GCSqGSIb3DQEBCwUAA4ICAQCXDag9YwLY5LNzkxi6 17 | r1X7YLaWdy3gC9z74zTGyRUmKFhq2Zs5R56BvUSmGx0YDKijpfmDf/qG0sn+SUAz 18 | R1Hba8mkBzc+5s3qtOFj/MDqNbMEcojHo7OLlOstOUzLCBpRWY5D63qcTTJOkfR0 19 | KAQieG2nMdOFnMzB+1r1Km1xHnS1pCCCaVzVscPZ7PgtLmtvwbhPfJCpRCwRBAAc 20 | ZJxHxHand77kNe9+p77XOHrS9rHcQupuRuFi97egt4ycBMAJ+U65REkDdqiInCBx 21 | aeU4d4hWj8AfeF9ISzlXZUwPF6HsGyhuK9JrifxQlRQFY7Q7pMoUZ11/GUDMVAHh 22 | RAACGFtxoQbs8qTr631Vjx6gzpHoBaJCV9VNCwumu/kJDzcDVdi/QQA1pcj4rrP0 23 | Dqx7bHprbV8XoD0XFfA0WsxOv/JZJL+E4TBIWDoc9hNZrmjXtK8QYx7H2Wv+lDmP 24 | /GygLCdlveJqWlSYnTTOZHY5DoNrX+UxjkjyzgoCtnsr7+pUNYiYW7MarxyhFiq+ 25 | JBhfxmOOpjxoNV5CpBJF3vnauSYEoehU5kMf2bZfqmtROIqRNKufhPlo4w5pL1vH 26 | +z2hFQ5t9bvYlZPwU1cqYDzGOFr2kQweXVy9mQnSBvBUHR37VCMXZ6jKacQActj+ 27 | l4YUJP2y5GKxKoL7SHWcvP+WRg== 28 | -----END CERTIFICATE REQUEST----- 29 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Peers.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 46 | 47 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/UserRealmResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | import javax.servlet.ServletContext; 25 | import javax.ws.rs.GET; 26 | import javax.ws.rs.Path; 27 | import javax.ws.rs.Produces; 28 | import org.carapaceproxy.core.HttpProxyServer; 29 | import org.carapaceproxy.user.UserRealm; 30 | 31 | /** 32 | * Access the users API 33 | * 34 | * @author matteo.minardi 35 | */ 36 | @Path("/users") 37 | @Produces("application/json") 38 | public class UserRealmResource { 39 | 40 | @javax.ws.rs.core.Context 41 | ServletContext context; 42 | 43 | @Path("/all") 44 | @GET 45 | public Collection getAll() { 46 | HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 47 | UserRealm userRealm = server.getRealm(); 48 | if (userRealm == null) { 49 | return new ArrayList<>(); 50 | } 51 | return userRealm.listUsers(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/EndpointStats.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy; 21 | 22 | import java.util.concurrent.atomic.AtomicInteger; 23 | import java.util.concurrent.atomic.AtomicLong; 24 | import org.carapaceproxy.core.EndpointKey; 25 | 26 | /** 27 | * Stats about an endpoint 28 | * 29 | * @author enrico.olivelli 30 | */ 31 | public class EndpointStats { 32 | 33 | private final EndpointKey key; 34 | private final AtomicInteger totalRequests = new AtomicInteger(); 35 | private final AtomicLong lastActivity = new AtomicLong(); 36 | 37 | public EndpointStats(final EndpointKey key) { 38 | this.key = key; 39 | } 40 | 41 | public String toString() { 42 | return "EndpointStats(key=" + this.key + ", totalRequests=" + this.totalRequests + ", lastActivity=" + this.lastActivity + ")"; 43 | } 44 | 45 | public AtomicInteger getTotalRequests() { 46 | return this.totalRequests; 47 | } 48 | 49 | public AtomicLong getLastActivity() { 50 | return this.lastActivity; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/user/SimpleUserRealm.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.user; 21 | 22 | import java.util.Arrays; 23 | import java.util.Collection; 24 | import java.util.Collections; 25 | import org.carapaceproxy.configstore.ConfigurationStore; 26 | import org.carapaceproxy.server.config.ConfigurationNotValidException; 27 | 28 | /** 29 | * Simple implementation of an {@link UserRealm} that has only one user and it's 30 | * always authorized. 31 | * 32 | * @author matteo.minardi 33 | */ 34 | public class SimpleUserRealm implements UserRealm { 35 | 36 | public static final String USERNAME = "admin"; 37 | 38 | @Override 39 | public void configure(ConfigurationStore properties) throws ConfigurationNotValidException { 40 | } 41 | 42 | @Override 43 | public Collection listUsers() { 44 | return Collections.unmodifiableCollection(Arrays.asList(USERNAME)); 45 | } 46 | 47 | @Override 48 | public String login(String username, String password) { 49 | return USERNAME; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/filters/XForwardedForRequestFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.filters; 21 | 22 | import java.net.InetSocketAddress; 23 | import org.carapaceproxy.core.ProxyRequest; 24 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 25 | 26 | /** 27 | * Add a X-Forwarded-For Header 28 | */ 29 | @Deprecated 30 | public class XForwardedForRequestFilter extends BasicRequestFilter { 31 | 32 | @Deprecated 33 | public static final String TYPE = "add-x-forwarded-for"; 34 | 35 | public XForwardedForRequestFilter(RequestMatcher matcher) { 36 | super(matcher); 37 | } 38 | 39 | @Override 40 | public void apply(ProxyRequest request) { 41 | if (!checkRequestMatching(request)) { 42 | return; 43 | } 44 | request.getRequestHeaders().remove("X-Forwarded-For"); 45 | InetSocketAddress address = request.getRemoteAddress(); 46 | request.getRequestHeaders().add("X-Forwarded-For", address.getAddress().getHostAddress()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/localhost.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIE4TCCAskCAQAwgYExCzAJBgNVBAYTAklUMRIwEAYDVQQIDAlsb2NhbGhvc3Qx 3 | EjAQBgNVBAcMCWxvY2FsaG9zdDEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0 4 | ZDESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglsb2NhbGhvc3Qw 5 | ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDYgH5Q00H62+FWcG9qp11y 6 | UrNCNL/C9kn5Lf7J6tpclnqFY8NeARyBe3GtqbHNtmT07K+XA/V9JH2Wku+5T7Ro 7 | hZjBoCcbT5SD8wr3coAv089xRJiAc5/Q12kCS1hSfTg55INCqwzuAjBF2aO8/uAm 8 | B9HkmtZTxHdUKmhK+UH7Vzr9EaIOWi/LOqG1LVA0faS7hKsgjEJ77mJka0sF66nL 9 | EcyNY3Md5EFAkxFG2bKbnLadNXeBcJz7/jCyOvTIRIHGPK2kiKnq7yWvOjEaLqn5 10 | 1GVS4mZqUD/AUVXAoPxFBNsG0F3Tjq6u6ffFgiOM3ue5ACwiinvoacOHKraWjVvM 11 | fLQoS+DSTdz+2xXXoBhqFyvcFSUBqOcLmJ1fCIr5vtte8NbkVi3GUA0TNeU7Zqzg 12 | u5fk5c4lPiojClpkfb8ekcN7JysPSIyU2hhASM1Osps0bCTyMEApBr4qStraAw/S 13 | X6PB6XGSNeZ38FH2nR1dsPCGoN1InwQqPWFjzvIrYX9h21dHPgIsoleF+7pHb/Zc 14 | LbmEd4yMeV7s8eR7GHQ4YjsYr+8vEg99CZlkRYaNT3I+0bxA28Pe74vEVtGFSYDy 15 | iqsa+k6GhdgVrBoYrmjAPe5Qruh76G5R1tkOiq/k0qI/91I/FpSn6tQR6NRUVugN 16 | ut8AtRQyQnvG4itlO1C9eQIDAQABoBowGAYJKoZIhvcNAQkHMQsMCXRlc3Rwcm94 17 | eTANBgkqhkiG9w0BAQsFAAOCAgEALROMqr95cA8ROPW+iiksmJj7j4XHt0doVUz/ 18 | UuxdO0tmJDqJJRvSYli8uSHV73LU2EeZKug1HGlHanMEp9ImQbBJoPCueAUu/4xz 19 | lhHji8s70wc6vQlsEmZEzayxLHsIf31yibvPkq2TgtrJuSr9p7CVc3DJyJzQqrpI 20 | kZie/JUshk82cKxHqDJhjxi2Z/U2sf0AcdGj4Wb8wC+Z9kzlJgUAShQPTSyc6Hu5 21 | 5IvYRdUs0Ssn/HUCWV1WbLv+QybHegML1oqJoTZNPXGhSxs5x9Whu6+VrBb2xP9q 22 | buK7rwtp9E+hZOyM3k6qL3e1e6Eme7qnZy9xWxFy8NEyIFcXWC3I8hvykIYFzS8Z 23 | qWgSn1LEw2L8mSVCOaJ6N/BiQ26iepvQRr9r7LMnfEYtO6ArfzF6ETdEmzGvTeT2 24 | ehK1SW0E1k7fpRu28CiKLQkvcGtrSIfDzANKY7xy57G0n4IbWYK1cFNAzpauxBVl 25 | t320Vjxmrv17NINz7XGO5AVyc1xY17JD41Eq9e3xE2GIA2LiRA5cBYZWGW+Ehqjx 26 | MG2VlECyPLmL31MD0DVCUYDhuEUKygxglWJ9IgiB/c1fFgF6Gj9x7rKow0wylCs+ 27 | W5XR5T/YIjvVRji5RYSzTpH+jWW4xRJn92hmMnyh3SI0Q0dtl2xrU0Lxa15D0Z84 28 | 0zCzoDg= 29 | -----END CERTIFICATE REQUEST----- 30 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/DirectorConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * A director is a group of backends 27 | * 28 | * @author enrico.olivelli 29 | */ 30 | public class DirectorConfiguration { 31 | 32 | private final String id; 33 | private final List backends = new ArrayList<>(); 34 | 35 | public static final String DEFAULT = "*"; 36 | 37 | public static final String ALL_BACKENDS = "*"; 38 | 39 | public DirectorConfiguration(String id) { 40 | this.id = id; 41 | } 42 | 43 | public DirectorConfiguration addBackend(String id) { 44 | if (!backends.contains(id)) { 45 | backends.add(id); 46 | } 47 | return this; 48 | } 49 | 50 | public String getId() { 51 | return id; 52 | } 53 | 54 | public List getBackends() { 55 | // no copy for efficiency, at runtime this bean is immutable 56 | return backends; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/client/ConnectionsManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.client; 21 | 22 | import org.carapaceproxy.core.EndpointKey; 23 | import org.carapaceproxy.core.RuntimeServerConfiguration; 24 | 25 | /** 26 | * Handles connection to all the endpoints 27 | * 28 | * @author enrico.olivelli 29 | */ 30 | public interface ConnectionsManager extends AutoCloseable { 31 | 32 | /** 33 | * Obtain a connection to the requested endpoint. Connections are pooled, so 34 | * the returned object MUST be returned to the pool 35 | * 36 | * @param key 37 | * @return 38 | * @throws org.carapaceproxy.client.EndpointNotAvailableException 39 | */ 40 | EndpointConnection getConnection(EndpointKey key) throws EndpointNotAvailableException; 41 | 42 | void start(); 43 | 44 | @Override 45 | void close(); 46 | 47 | ConnectionsManagerStats getStats(); 48 | 49 | /** 50 | * Apply new configuration an runtime 51 | * 52 | * @param configuration 53 | */ 54 | void applyNewConfiguration(RuntimeServerConfiguration configuration); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/AndRequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | import java.util.List; 23 | import java.util.stream.Collectors; 24 | 25 | /** 26 | * 27 | * Matcher for composing AND expressions with other matchers. 28 | * 29 | * @author paolo.venturi 30 | */ 31 | public class AndRequestMatcher implements RequestMatcher { 32 | 33 | private final List matchers; 34 | 35 | public AndRequestMatcher(List matchers) { 36 | this.matchers = matchers; 37 | } 38 | 39 | @Override 40 | public boolean matches(MatchingContext context) { 41 | for (RequestMatcher matcher : matchers) { 42 | if (!matcher.matches(context)) { 43 | return false; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | @Override 50 | public String getDescription() { 51 | return matchers.stream() 52 | .map(RequestMatcher::getDescription) 53 | .collect(Collectors.joining(" and ")); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/core/ForwardedStrategy.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.core; 2 | 3 | import static org.carapaceproxy.core.ForwardedStrategies.StaticStrategies; 4 | import com.google.common.net.HttpHeaders; 5 | import io.netty.handler.codec.http.HttpRequest; 6 | import java.util.Set; 7 | import java.util.function.BiFunction; 8 | import org.carapaceproxy.configstore.ConfigurationStore; 9 | import org.carapaceproxy.core.ForwardedStrategies.IfTrusted; 10 | import reactor.netty.http.server.ConnectionInfo; 11 | 12 | public sealed interface ForwardedStrategy extends BiFunction permits StaticStrategies, IfTrusted { 13 | 14 | /** 15 | * Choose a strategy to handle {@value HttpHeaders#X_FORWARDED_FOR} header given and optional set of trusted IPs. 16 | * 17 | * @param name the name of the strategy from the {@link ConfigurationStore config} 18 | * @param trustedIps an optional set of trusted IPs from the {@link ConfigurationStore config} 19 | * @return the appropriate strategy object 20 | */ 21 | static ForwardedStrategy of(final String name, final Set trustedIps) { 22 | if (StaticStrategies.DROP.name().equals(name)) { 23 | return ForwardedStrategies.drop(); 24 | } 25 | if (StaticStrategies.PRESERVE.name().equals(name)) { 26 | return ForwardedStrategies.preserve(); 27 | } 28 | if (StaticStrategies.REWRITE.name().equals(name)) { 29 | return ForwardedStrategies.rewrite(); 30 | } 31 | if (IfTrusted.NAME.equals(name)) { 32 | return ForwardedStrategies.ifTrusted(trustedIps); 33 | } 34 | throw new IllegalArgumentException("Unexpected forwarded strategy: " + name); 35 | } 36 | 37 | /** 38 | * Get a name for the strategy. 39 | * 40 | * @return the name 41 | * @see Enum#name() 42 | */ 43 | String name(); 44 | } 45 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Routes.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/ia.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFOzCCAyMCAQEwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCSVQxDjAMBgNV 3 | BAgMBUl0YWx5MRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1 4 | bHQgQ29tcGFueSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xODA1MjgxNDM4 5 | MjlaFw0yMDA1MjcxNDM4MjlaMGExCzAJBgNVBAYTAlhYMQ4wDAYDVQQIDAVJdGFs 6 | eTEOMAwGA1UEBwwFSXRhbHkxDjAMBgNVBAoMBUl0YWx5MQ4wDAYDVQQLDAVJdGFs 7 | eTESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC 8 | CgKCAgEAunQDCXeDZED5ICeCR+WsrrZYG3EUyqC/R9hQ63VxMgUDVbb1Q0s0ESnE 9 | 4Q/Z95wa5chykCmQY5Q0eltClyE23uWb7uNfUClRhVlx9pRff9lMGu8fhmcmxME3 10 | zjaKjV4j2L59XScfWQNT88wA/i1ycp/MtrKeRvog5LID4PA8UMA4vpe2aSM9cPU0 11 | /pbMp4aeZ989Bo5oOKxh/fjPPB0zPfnTyPTAZJZ6DXPOewjnJO+sOvhoVhx9ZmU8 12 | ApcozIOKcaTQ3jv76QMVvUm7Ub+a9cEw9QweE5+hV8NVdR2qPp+6VT2sP19Xqmsx 13 | O8R1soksWPjYUcxzIHFvdoyCBnfMHlRE9UC46yXCLEYxDi+Of4krfKbKSq0f3nyV 14 | V81KxQh3Ian4ejweRWapRxH+BJg3tnKQF4FdcQTIyrOg1WXHd+xHi33XqBptDJaG 15 | ntbg5EMCA1cokdQFANsHOkI1QHn8ypdhcmaKVtErOsxsejNjYYf4gYQvwUpFz9yh 16 | sxuZkB1symKwqQ0DJH5xD7iZV4MVm6apc8IO7+M6qdFVmbuFgW4X6ONpuDsg4WuN 17 | p9X6aIoOf1JTTKnD8o17j16pona0hj0jpwT1oVOSGZ6z7ouvYf6B6AcunViiO6kw 18 | pv46KAiDS+gLxZgDU8ygTSbkb0F+iYO6IUkzwpwx/3fQXA1NfkcCAwEAATANBgkq 19 | hkiG9w0BAQsFAAOCAgEAdcxV4KU/5mqvJL3x7nJqt04CB8UOX7dLnVyP3sdgd7DU 20 | 4tu9armxs4R+B4i7VkbqlH5xw84mEPTpzuBvBP34/HpGICsYVZsiRCL7V2kNZpkN 21 | heqJePq5e1i2C4tNaslyArjzD2+mGLE3/0/jHBH8McZ/IhHBv2eqtpjhr3ZOFlFs 22 | LQVDa7eyevxjFtDsD4wZASYGpyHypyZW/62nyfVgGLGRGv+qZRoub1jpuqE7em31 23 | AWG9UmpsFUoYyG4KWzCmrqMZM9rnkINDmfokPbSE/O+9YF3ro9PwqawMuPtSLIHR 24 | i/hAJUP4/5GN9RJA8yNoReOjja+Sati+lR9z14QsyAofLP1aFu7R5Mq9s7aylDd+ 25 | 5fOtwrzfeJlXeRLAqEqlPoXTDvvb4IzBYzLm4gltdB/OSLP5t1kzNzmdkd10KaFD 26 | ssK63I1cGjpyt5/dkUWKAoVDBAqnrZhtdshIKxTfnT9xqnKFS4zHpdXjjMFYrsHB 27 | egiDkXVXzk7HyIIeQ06wr9VT9CDOjtT2bXQHyul6j8OFr5miUNJ0XEqNQxhTDwiA 28 | Yj4XdOYd/Iqg+VgRtEkJGu7aMcxx8Ikckr4uUsB90hKvhSYjpmXiZWjb+hy1Rqeh 29 | +1QXFtAV1ROSL6kmH5hF8+F+p/YOvXvwI7YwRvfE6gqM6qGLOFB/iu7aiqtP+T0= 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import "./app.scss"; 5 | import { toBooleanSymbol } from "./lib/formatter"; 6 | import { formatTimestamp } from "./lib/formatter"; 7 | import BootstrapVue from "bootstrap-vue"; 8 | import "bootstrap/dist/css/bootstrap.css"; 9 | import "bootstrap-vue/dist/bootstrap-vue.css"; 10 | 11 | // Icons 12 | import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; 13 | import { library } from "@fortawesome/fontawesome-svg-core"; 14 | import { 15 | faAngleRight, 16 | faArchive, 17 | faBolt, 18 | faChartBar, 19 | faCircleInfo, 20 | faCrosshairs, 21 | faDatabase, 22 | faDoorOpen, 23 | faFileSignature, 24 | faFilter, 25 | faHeading, 26 | faHome, 27 | faInfo, 28 | faMapSigns, 29 | faNetworkWired, 30 | faServer, 31 | faSlidersH, 32 | faUsers 33 | } from "@fortawesome/free-solid-svg-icons"; 34 | library.add(faAngleRight); 35 | library.add(faArchive); 36 | library.add(faBolt); 37 | library.add(faChartBar); 38 | library.add(faCircleInfo); 39 | library.add(faCrosshairs); 40 | library.add(faDatabase); 41 | library.add(faDoorOpen); 42 | library.add(faFileSignature); 43 | library.add(faFilter); 44 | library.add(faHeading); 45 | library.add(faHome); 46 | library.add(faInfo); 47 | library.add(faMapSigns); 48 | library.add(faNetworkWired); 49 | library.add(faServer); 50 | library.add(faSlidersH); 51 | library.add(faUsers); 52 | 53 | Vue.component("font-awesome-icon", FontAwesomeIcon); 54 | 55 | // Boostrap Vue 56 | Vue.use(BootstrapVue); 57 | 58 | Vue.config.productionTip = false; 59 | 60 | // Filters 61 | Vue.filter("symbolFormat", value => { 62 | return toBooleanSymbol(value); 63 | }); 64 | Vue.filter("dateFormat", value => { 65 | if (!value || value <= 0) { 66 | return ""; 67 | } 68 | return formatTimestamp(value); 69 | }); 70 | 71 | new Vue({ 72 | router: router, 73 | render: h => h(App) 74 | }).$mount("#app"); 75 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/ForceHeadersAPIRequestsFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.io.IOException; 23 | import javax.servlet.Filter; 24 | import javax.servlet.FilterChain; 25 | import javax.servlet.FilterConfig; 26 | import javax.servlet.ServletException; 27 | import javax.servlet.ServletRequest; 28 | import javax.servlet.ServletResponse; 29 | import javax.servlet.http.HttpServletResponse; 30 | 31 | /** 32 | * No cache API responses 33 | * 34 | * @author enrico.olivelli 35 | */ 36 | public class ForceHeadersAPIRequestsFilter implements Filter { 37 | 38 | @Override 39 | public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) 40 | throws IOException, ServletException { 41 | HttpServletResponse resp = (HttpServletResponse) response; 42 | resp.setHeader("Cache-Control", "no-cache"); 43 | resp.setHeader("Access-Control-Allow-Origin", "*"); 44 | chain.doFilter(request, response); 45 | } 46 | 47 | @Override 48 | public void init(FilterConfig filterConfig) throws ServletException { 49 | } 50 | 51 | @Override 52 | public void destroy() { 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /carapace-server/src/main/resources/bin/java-utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Licensed to Diennea S.r.l. under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. Diennea S.r.l. licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | 20 | if [ $# -lt 1 ]; 21 | then 22 | echo "USAGE: $0 [-daemon] SERVICETYPE [jvmargs]" 23 | exit 1 24 | fi 25 | 26 | 27 | if [ -z "$BASE_DIR" ]; 28 | then 29 | BASE_DIR="`dirname \"$0\"`" 30 | BASE_DIR="`( cd \"$BASE_DIR/..\" && pwd )`" 31 | fi 32 | 33 | cd $BASE_DIR 34 | . $BASE_DIR/bin/setenv.sh 35 | 36 | 37 | CLASSPATH= 38 | 39 | for file in $BASE_DIR/lib/*.jar; 40 | do 41 | CLASSPATH=$CLASSPATH:$file 42 | done 43 | 44 | for file in $BASE_DIR/extra/*.jar; 45 | do 46 | CLASSPATH=$CLASSPATH:$file 47 | done 48 | 49 | 50 | if [ -z "$JAVA_OPTS" ]; then 51 | JAVA_OPTS="" 52 | fi 53 | 54 | JAVA="$JAVA_HOME/bin/java" 55 | 56 | while [ $# -gt 0 ]; do 57 | COMMAND=$1 58 | case $COMMAND in 59 | -daemon) 60 | DAEMON_MODE="true" 61 | shift 62 | ;; 63 | *) 64 | break 65 | ;; 66 | esac 67 | done 68 | 69 | SERVICE=$1 70 | shift 71 | 72 | if [ "x$DAEMON_MODE" = "xtrue" ]; then 73 | CONSOLE_OUTPUT_FILE=$SERVICE.service.log 74 | nohup $JAVA -cp $CLASSPATH $JAVA_OPTS "$@" >> "$CONSOLE_OUTPUT_FILE" 2>&1 < /dev/null & 75 | RETVAL=$? 76 | else 77 | exec $JAVA -cp $CLASSPATH $JAVA_OPTS "$@" 78 | RETVAL=$? 79 | fi 80 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/localhost.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFgDCCA2gCCQCYkuRWgRtpszANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMC 3 | SVQxEjAQBgNVBAgMCWxvY2FsaG9zdDESMBAGA1UEBwwJbG9jYWxob3N0MRwwGgYD 4 | VQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QxGDAW 5 | BgkqhkiG9w0BCQEWCWxvY2FsaG9zdDAeFw0xNzA4MjgwOTU0MzRaFw0yNzA4MjYw 6 | OTU0MzRaMIGBMQswCQYDVQQGEwJJVDESMBAGA1UECAwJbG9jYWxob3N0MRIwEAYD 7 | VQQHDAlsb2NhbGhvc3QxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxEjAQ 8 | BgNVBAMMCWxvY2FsaG9zdDEYMBYGCSqGSIb3DQEJARYJbG9jYWxob3N0MIICIjAN 9 | BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2IB+UNNB+tvhVnBvaqddclKzQjS/ 10 | wvZJ+S3+yeraXJZ6hWPDXgEcgXtxramxzbZk9OyvlwP1fSR9lpLvuU+0aIWYwaAn 11 | G0+Ug/MK93KAL9PPcUSYgHOf0NdpAktYUn04OeSDQqsM7gIwRdmjvP7gJgfR5JrW 12 | U8R3VCpoSvlB+1c6/RGiDlovyzqhtS1QNH2ku4SrIIxCe+5iZGtLBeupyxHMjWNz 13 | HeRBQJMRRtmym5y2nTV3gXCc+/4wsjr0yESBxjytpIip6u8lrzoxGi6p+dRlUuJm 14 | alA/wFFVwKD8RQTbBtBd046urun3xYIjjN7nuQAsIop76GnDhyq2lo1bzHy0KEvg 15 | 0k3c/tsV16AYahcr3BUlAajnC5idXwiK+b7bXvDW5FYtxlANEzXlO2as4LuX5OXO 16 | JT4qIwpaZH2/HpHDeycrD0iMlNoYQEjNTrKbNGwk8jBAKQa+Kkra2gMP0l+jwelx 17 | kjXmd/BR9p0dXbDwhqDdSJ8EKj1hY87yK2F/YdtXRz4CLKJXhfu6R2/2XC25hHeM 18 | jHle7PHkexh0OGI7GK/vLxIPfQmZZEWGjU9yPtG8QNvD3u+LxFbRhUmA8oqrGvpO 19 | hoXYFawaGK5owD3uUK7oe+huUdbZDoqv5NKiP/dSPxaUp+rUEejUVFboDbrfALUU 20 | MkJ7xuIrZTtQvXkCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEADrHDjHPkZdHHVwlw 21 | DQ/54J9bmjjTTsWX13WkWJg7wAefFN+JBNmz8Hrc+zq4HJ3p897b3I+ZebqfRGwe 22 | xC+ifnrxhHoR0iBteV++M19KULu3LLCg0s0nH+NIw/wBvSwbjs9AIxcGaIDxYhO0 23 | LfnFX9UnKPnDhCxGAv3Pgge/ufGQdH6mMGj17fFyZFd+vXtAXd6B2koKPheir67l 24 | ZM8sEJv4NbEaPvLF8pRkERLTAWh62e+JupNq9v2uTghc2+nf1LY24n6eq1cDFHcT 25 | T/vyMFuz6/ZPLJZtVhyecgpxm9/xcDSx7mm8BRKnIZspq20/RbMEjit2aK2lPvmK 26 | w8TnpoOAroIifJy9QJScU2gEtYgNJTYNijVELDZL8oTqhqpZ8hEK/SaIL3ZOdJbP 27 | E/0emtASCr6623lGOY9J1OEuAeirlnmuc4PeLXXIThJJbdOMefLKQpIGAViNNTXa 28 | ng0fGwhL4svPzMBkRSMoxxVTngw1Z//rHCQ6QLus82vI02SKBOFRw3Ely4WLYQPg 29 | esjuD86K1RevfJmJCFI6096eDXieFNBbbUwZHN9BlV9SP/gX+phmtQb4Wx0k0BZd 30 | p+N2CwBOgZRrouBU3C+xHThurgAUpsSft6TMeu8WwnCItB2wcKFwZz0COcPEFEgx 31 | GrqZOd5vWBHUBSTTWPpuT4sH2wI= 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /carapace-server/src/test/resources/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFojCCA4qgAwIBAgIJAI6iDlWsvtPwMA0GCSqGSIb3DQEBCwUAMGYxCzAJBgNV 3 | BAYTAklUMQ4wDAYDVQQIDAVJdGFseTEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRww 4 | GgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3Qw 5 | HhcNMTgwNTI4MTQzODAyWhcNMjMwNTI4MTQzODAyWjBmMQswCQYDVQQGEwJJVDEO 6 | MAwGA1UECAwFSXRhbHkxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwT 7 | RGVmYXVsdCBDb21wYW55IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkq 8 | hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArtftMM+DLoHfX1eABbSkewi9c75WNuyf 9 | o+STbryC68/hMIuKB0UmrxlX+E0yN6SVvwBk+VXwtorGYatmHkDmL4Lo33z/5aqT 10 | LUpQaI77aa05IeKUOy2QZlP0jN4yXiwP+Xjkwt+Y7x3MxNMl/6Dg1dB9UGNesHdO 11 | Jbzz66kEg3HN7ZceWpP4kE2OtsUY5PtZu2STJKzaR3deTzSsiux/Cux+EbRMkf+e 12 | o44NC5y1Gmt5pxpfuOy7RmBJGNXkreoJ50RRiIkTsh9Us/xwP37KjkCqHWkxNgTN 13 | h1KwE5a2LY2qF4G8L+D3NnPCx5+6pkohgoxljcH9TMLpJRFJOxSK15itQ1Zk5X8N 14 | a1Y/M+FwhAniWM2gp5TlHV40Vhy0nsmUs4CXKYdsqiuAUvn5pjPlAJTmzaZ3lzyp 15 | fXjjKoXfps7BG4+A40ue+OwgA66tvgg5rmvalLk8fiGYnKiFjYGwwEvSFc0cTDHw 16 | k27qVFs8m25gLJmxSHmyS1f+Rv2RLpDTCwI39orkR7BDnF4Uh3Ob4QgkAMeDF3Y6 17 | aVDth0jWSgw0FnPue3zGBbJ+VVBu5U6rahVFbKicpOLqlZi7fTUvKskf/adhurIu 18 | dg7yvdylQWa8anLqZ8xKjC+KkUysjxP7A1/K+npV9sVdjGinzAaFvs2xbNm5Yy0o 19 | lN0AgeVm16MCAwEAAaNTMFEwHQYDVR0OBBYEFIkk2hNEA1vphTNz59N563gKyF7i 20 | MB8GA1UdIwQYMBaAFIkk2hNEA1vphTNz59N563gKyF7iMA8GA1UdEwEB/wQFMAMB 21 | Af8wDQYJKoZIhvcNAQELBQADggIBAHJU4T4oJBN1v6oAaKGRw7EqVtKZgLyUa/Ha 22 | 012OwNBxayRpPj6SeXh97vk9zdLtC+BsdI2CsGYJMuZNI9z0g/AG/vhJZfcd0nQv 23 | 6bhNMCxkjZn3e5PqLbisbIKnLMRIyfiKLsOZMJACyAJo97Pmte1nd3Of+rggGA5C 24 | 1R/5s8w4h7KQmf1UzZn0nuBNViOGkmsybneiIDG78ZD2t4qXjdYNq043r/eoVzoE 25 | rTxVw7+54E0PwFSGrf8Kb1XLCshTSbBLFvoaMi6PR2pQ96KaXlz0iPtM2uS3UjNZ 26 | SuDTeBoECCzVMZLMFTdTOpqVhp2Qo7a4Ex6AIsJ6E7j5uxTLYHbj/5uUaze3cOEC 27 | QX4cx2EtIV5rb387Bu5e+7LL6mC9hxkMe5pACFVicHXmizVt8G/VwgWh4p6Bu+Nn 28 | d18/IbBt5BzK/oACQPMB3GDarQVE9m/+oNr03p79lhdNuf1YKXW5dEFIxS8HdySh 29 | nmVWEeGTpBA/ynVo0od+Pbx6MdOdV0RWDI0z9hvZlZLi2xcEeRwk04DnKhESxPzo 30 | 152WsQxZ5rzcyRX27HEzM2Pm8kQiySDgIwq2vUlCJpdqgC1LKd2QqcsQJobjv6JZ 31 | LQwmnl+p52nJTcnFX0TgVHU9wqd4OsaUlzDoiwHiRr+xM0XuWcfPbF14a8Rw4Ybu 32 | epcUTH+d 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/core/OcspSslHandler.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.core; 2 | 3 | import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; 4 | import io.netty.handler.ssl.SslContext; 5 | import io.netty.handler.ssl.SslHandler; 6 | import io.netty.util.AttributeKey; 7 | import java.io.IOException; 8 | import java.security.cert.Certificate; 9 | import java.util.function.Consumer; 10 | import org.carapaceproxy.server.certificates.ocsp.OcspStaplingManager; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | public class OcspSslHandler implements Consumer { 15 | private static final Logger LOG = LoggerFactory.getLogger(OcspSslHandler.class); 16 | private static final AttributeKey ATTRIBUTE = AttributeKey.valueOf(Listeners.OCSP_CERTIFICATE_CHAIN); 17 | 18 | private final SslContext sslContext; 19 | private final OcspStaplingManager ocspStaplingManager; 20 | 21 | public OcspSslHandler(final SslContext sslContext, final OcspStaplingManager ocspStaplingManager1) { 22 | this.sslContext = sslContext; 23 | this.ocspStaplingManager = ocspStaplingManager1; 24 | } 25 | 26 | @Override 27 | public void accept(final SslHandler sslHandler) { 28 | final Certificate cert = sslContext.attributes().attr(ATTRIBUTE).get(); 29 | if (cert == null) { 30 | LOG.error("Cannot set OCSP response without the certificate"); 31 | return; 32 | } 33 | if (!(sslHandler.engine() instanceof ReferenceCountedOpenSslEngine engine)) { 34 | LOG.error("Unexpected SSL handler type: {}", sslHandler.engine()); 35 | return; 36 | } 37 | try { 38 | final byte[] ocspResponse = ocspStaplingManager.getOcspResponseForCertificate(cert); 39 | if (ocspResponse == null) { 40 | LOG.error("No OCSP response for certificate: {}", cert); 41 | return; 42 | } 43 | engine.setOcspResponse(ocspResponse); 44 | } catch (IOException ex) { 45 | LOG.error("Error setting OCSP response.", ex); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/OrRequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | import java.util.List; 23 | import java.util.stream.Collectors; 24 | 25 | /** 26 | * 27 | * Matcher for composing OR expressions with other matchers. 28 | * 29 | * @author paolo.venturi 30 | */ 31 | public class OrRequestMatcher implements RequestMatcher { 32 | 33 | private final List matchers; 34 | private final boolean wrap; 35 | 36 | public OrRequestMatcher(List matchers, boolean wrap) { 37 | this.matchers = matchers; 38 | this.wrap = wrap; 39 | } 40 | 41 | @Override 42 | public boolean matches(MatchingContext context) { 43 | for (RequestMatcher matcher : matchers) { 44 | if (matcher.matches(context)) { 45 | return true; 46 | } 47 | } 48 | return false; 49 | } 50 | 51 | @Override 52 | public String getDescription() { 53 | String desc = wrap ? "(" : ""; 54 | desc += matchers.stream() 55 | .map(RequestMatcher::getDescription) 56 | .collect(Collectors.joining(" or ")); 57 | desc += wrap ? ")" : ""; 58 | 59 | return desc; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/ApplicationConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.util.Set; 23 | import javax.ws.rs.ApplicationPath; 24 | import javax.ws.rs.core.Application; 25 | 26 | /** 27 | * Configuration of the REST API 28 | * 29 | * @author enrico.olivelli 30 | */ 31 | @ApplicationPath("api") 32 | public class ApplicationConfig extends Application { 33 | 34 | @Override 35 | public Set> getClasses() { 36 | Set> resources = new java.util.HashSet<>(); 37 | resources.add(CacheResource.class); 38 | resources.add(ServiceUpResource.class); 39 | resources.add(BackendsResource.class); 40 | resources.add(ConnectionPoolsResource.class); 41 | resources.add(RoutesResource.class); 42 | resources.add(ActionsResource.class); 43 | resources.add(DirectorsResource.class); 44 | resources.add(ConfigResource.class); 45 | resources.add(ListenersResource.class); 46 | resources.add(CertificatesResource.class); 47 | resources.add(RequestFiltersResource.class); 48 | resources.add(UserRealmResource.class); 49 | resources.add(MetricsResource.class); 50 | resources.add(ClusterResource.class); 51 | resources.add(HeadersResource.class); 52 | return resources; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Listeners.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/certificates/DynamicCertificateState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Diennea S.r.l. under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. Diennea S.r.l. licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | package org.carapaceproxy.server.certificates; 21 | 22 | /** 23 | * 24 | * @author paolo.venturi 25 | */ 26 | public enum DynamicCertificateState { 27 | /** Certificate waiting for issuing/renews */ 28 | WAITING, 29 | 30 | /** Certificate domain reported as unreachable for issuing/renewing */ 31 | DOMAIN_UNREACHABLE, 32 | 33 | /** For DNS-01 challenge, wait for DNS propagation before checking CA. */ 34 | DNS_CHALLENGE_WAIT, 35 | 36 | /** Challenge verification by LE pending */ 37 | VERIFYING, 38 | 39 | /** Challenge succeeded */ 40 | VERIFIED, 41 | 42 | /** Certificate order pending */ 43 | ORDERING, 44 | 45 | /** Challenge/order failed */ 46 | REQUEST_FAILED, 47 | 48 | /** Certificate available (saved) and not expired */ 49 | AVAILABLE, 50 | 51 | /** Certificate expired */ 52 | EXPIRED; 53 | 54 | public String toStorableFormat() { 55 | return this.name(); 56 | } 57 | 58 | public static DynamicCertificateState fromStorableFormat(String state) { 59 | return DynamicCertificateState.valueOf(state); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return super.toString().toLowerCase().replaceAll("_", " "); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/requestmatcher/RegexpRequestMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.mapper.requestmatcher; 21 | 22 | import java.util.regex.Pattern; 23 | import java.util.regex.PatternSyntaxException; 24 | import org.carapaceproxy.server.config.ConfigurationNotValidException; 25 | 26 | /** 27 | * Matcher by Regular Expression 28 | * 29 | * @author paolo.venturi 30 | */ 31 | public class RegexpRequestMatcher implements RequestMatcher { 32 | 33 | private final String name; 34 | private final Pattern expression; 35 | 36 | public RegexpRequestMatcher(String name, String expression) throws ConfigurationNotValidException { 37 | this.name = name; 38 | try { 39 | this.expression = Pattern.compile(expression); 40 | } catch (PatternSyntaxException err) { 41 | throw new ConfigurationNotValidException(err); 42 | } 43 | } 44 | 45 | @Override 46 | public boolean matches(MatchingContext context) { 47 | return expression.matcher(context.getProperty(name)).matches(); 48 | } 49 | 50 | @Override 51 | public String getDescription() { 52 | return name + " ~ \"" + this.expression.toString() + "\""; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "RegexRequestMatcher{" + "regexp='" + expression + "'}"; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 45 | 46 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/SimpleHTTPResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Diennea S.r.l. under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. Diennea S.r.l. licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | package org.carapaceproxy; 21 | 22 | import io.netty.handler.codec.http.HttpResponseStatus; 23 | import java.util.List; 24 | import org.carapaceproxy.server.mapper.CustomHeader; 25 | 26 | /** 27 | * Static response to sent to clients. 28 | * 29 | * @author paolo.venturi 30 | */ 31 | public record SimpleHTTPResponse(int errorCode, String resource, List customHeaders) { 32 | 33 | public SimpleHTTPResponse { 34 | customHeaders = List.copyOf(customHeaders); 35 | } 36 | 37 | public SimpleHTTPResponse(final int errorCode, final String resource) { 38 | this(errorCode, resource, List.of()); 39 | } 40 | 41 | public static SimpleHTTPResponse notFound(final String resource) { 42 | return new SimpleHTTPResponse(HttpResponseStatus.NOT_FOUND.code(), resource); 43 | } 44 | 45 | public static SimpleHTTPResponse internalError(final String resource) { 46 | return new SimpleHTTPResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), resource); 47 | } 48 | 49 | public static SimpleHTTPResponse badRequest(final String resource) { 50 | return new SimpleHTTPResponse(HttpResponseStatus.BAD_REQUEST.code(), resource); 51 | } 52 | 53 | public static SimpleHTTPResponse serviceUnavailable(final String resource) { 54 | return new SimpleHTTPResponse(HttpResponseStatus.SERVICE_UNAVAILABLE.code(), resource); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.utils; 21 | 22 | /** 23 | * 24 | * @author matteo.minardi 25 | */ 26 | public class StringUtils { 27 | 28 | private static String htmlEncodeCharacters(final String s) { 29 | StringBuilder res = new StringBuilder(); 30 | for (char c : s.toCharArray()) { 31 | switch (c) { 32 | case '&': 33 | res.append("&"); 34 | break; 35 | case '<': 36 | res.append("<"); 37 | break; 38 | case '>': 39 | res.append(">"); 40 | break; 41 | case '\"': 42 | res.append("""); 43 | break; 44 | default: 45 | res.append(c); 46 | } 47 | } 48 | return res.toString(); 49 | } 50 | 51 | public static String htmlEncode(final String s) { 52 | if (s == null || s.isEmpty()) { 53 | return s; 54 | } 55 | return htmlEncodeCharacters(s); 56 | } 57 | 58 | public static String trimToNull(final String str) { 59 | if (str == null || str.isEmpty()) { 60 | return null; 61 | } 62 | return str.trim(); 63 | } 64 | 65 | public static boolean isBlank(final String str) { 66 | return str == null || str.isBlank(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/DirectorsResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import javax.servlet.ServletContext; 25 | import javax.ws.rs.GET; 26 | import javax.ws.rs.Path; 27 | import javax.ws.rs.Produces; 28 | import org.carapaceproxy.core.HttpProxyServer; 29 | 30 | /** 31 | * Access to configured directors 32 | * 33 | * @author paolo.venturi 34 | */ 35 | @Path("/directors") 36 | @Produces("application/json") 37 | public class DirectorsResource { 38 | 39 | @javax.ws.rs.core.Context 40 | ServletContext context; 41 | 42 | public static final class DirectorBean { 43 | private final String id; 44 | private final List backends; 45 | 46 | public DirectorBean(String id, List backends) { 47 | this.id = id; 48 | this.backends = backends; 49 | } 50 | 51 | public String getId() { 52 | return id; 53 | } 54 | 55 | public List getBackends() { 56 | return backends; 57 | } 58 | } 59 | 60 | @GET 61 | public List getAll() { 62 | final List directors = new ArrayList<>(); 63 | HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 64 | server.getMapper().getDirectors() 65 | .forEach((directorId, director) -> directors.add(new DirectorBean(directorId, director.getBackends()))); 66 | 67 | return directors; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /carapace-server/src/main/assemble/zip-assembly.xml: -------------------------------------------------------------------------------- 1 | 4 | release 5 | 6 | zip 7 | 8 | true 9 | 10 | 11 | src/main/resources 12 | / 13 | 14 | logback.xml 15 | 16 | 17 | 18 | src/main/resources/conf 19 | conf 20 | 21 | * 22 | 23 | 24 | 25 | src/main/resources/bin 26 | bin 27 | 777 28 | 29 | *.sh 30 | service 31 | 32 | 33 | 34 | target/generated-sources/license 35 | license 36 | 37 | * 38 | 39 | 40 | 41 | 42 | 43 | true 44 | true 45 | false 46 | lib 47 | 48 | 49 | true 50 | true 51 | true 52 | 53 | org.carapaceproxy:carapace-ui:war 54 | 55 | web 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/client/EndpointConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.client; 21 | 22 | import io.netty.handler.codec.http.HttpContent; 23 | import io.netty.handler.codec.http.HttpRequest; 24 | import io.netty.handler.codec.http.LastHttpContent; 25 | import org.carapaceproxy.core.EndpointKey; 26 | import org.carapaceproxy.core.ProxyRequestsManager; 27 | 28 | /** 29 | * A Connection to a specific endpoint. Connections are pooled and so they have 30 | * to be returned to the pool. A connection can be bound to at most one RequestHandler at a time. 31 | * 32 | * @author enrico.olivelli 33 | */ 34 | public interface EndpointConnection { 35 | 36 | public EndpointKey getKey(); 37 | 38 | /** 39 | * Start a request and bind the connection to the RequestHandler. 40 | */ 41 | public void sendRequest(HttpRequest request, ProxyRequestsManager handler); 42 | 43 | /** 44 | * Send other chunks (chunked payload from the client). 45 | * @param httpContent 46 | * @param handler 47 | */ 48 | public void sendChunk(HttpContent httpContent, ProxyRequestsManager handler); 49 | 50 | /** 51 | * Client finished its request. 52 | * @param msg 53 | * @param handler 54 | */ 55 | public void sendLastHttpContent(LastHttpContent msg, ProxyRequestsManager handler); 56 | 57 | /** 58 | * Connection is no more useful for the RequestHandler. 59 | * @param forceClose 60 | * @param handler 61 | * @param onReleasePerformed 62 | */ 63 | public void release(boolean forceClose, ProxyRequestsManager handler, Runnable onReleasePerformed); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/utils/TestUserRealm.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.utils; 21 | 22 | import java.util.Collection; 23 | import java.util.Collections; 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | import org.carapaceproxy.configstore.ConfigurationStore; 27 | import org.carapaceproxy.server.config.ConfigurationNotValidException; 28 | import org.carapaceproxy.user.UserRealm; 29 | 30 | /** 31 | * Test {@link UserRealm} that takes the users from the 32 | * {@link ConfigurationStore} 33 | * 34 | * @author matteo.minardi 35 | */ 36 | public class TestUserRealm implements UserRealm { 37 | 38 | private static final String PREFIX = "user."; 39 | 40 | private final Map users = new HashMap<>(); 41 | 42 | @Override 43 | public void configure(ConfigurationStore properties) throws ConfigurationNotValidException { 44 | properties.forEach(PREFIX, (k, v) -> { 45 | String username = k.replace(PREFIX, "").trim(); 46 | String password = v.trim(); 47 | 48 | users.put(username, password); 49 | }); 50 | } 51 | 52 | @Override 53 | public Collection listUsers() { 54 | return Collections.unmodifiableCollection(users.keySet()); 55 | } 56 | 57 | @Override 58 | public String login(String username, String password) { 59 | if (username == null || password == null) { 60 | return null; 61 | } 62 | String _password = users.get(username); 63 | if (password.equals(_password)) { 64 | return username; 65 | } 66 | return null; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/SafeBackendSelector.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.server.config; 2 | 3 | import static org.carapaceproxy.server.config.DirectorConfiguration.ALL_BACKENDS; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.SequencedCollection; 8 | import org.carapaceproxy.server.backends.BackendHealthStatus; 9 | import org.carapaceproxy.server.mapper.EndpointMapper; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | public class SafeBackendSelector implements BackendSelector { 14 | private static final Logger LOGGER = LoggerFactory.getLogger(SafeBackendSelector.class); 15 | private final EndpointMapper mapper; 16 | 17 | public SafeBackendSelector(final EndpointMapper mapper) { 18 | this.mapper = mapper; 19 | } 20 | 21 | @Override 22 | public List selectBackends(final String userId, final String sessionId, final String director) { 23 | final Map directors = mapper.getDirectors(); 24 | if (!directors.containsKey(director)) { 25 | LOGGER.error("Director \"{}\" not configured, while handling request userId={} sessionId={}", director, userId, sessionId); 26 | return List.of(); 27 | } 28 | final DirectorConfiguration directorConfig = directors.get(director); 29 | if (directorConfig.getBackends().contains(ALL_BACKENDS)) { 30 | return sortByConnections(mapper.getBackends().sequencedKeySet()); 31 | } 32 | return sortByConnections(directorConfig.getBackends()); 33 | } 34 | 35 | public List sortByConnections(final SequencedCollection backendIds) { 36 | return backendIds.stream().sorted(Comparator.comparingLong(this::connections)).toList(); 37 | } 38 | 39 | private long connections(final String backendId) { 40 | final BackendHealthStatus backendStatus = mapper.getBackendHealthManager().getBackendStatus(backendId); 41 | return switch (backendStatus.getStatus()) { 42 | case DOWN -> Long.MAX_VALUE; // backends that are down are put last, but not dropped 43 | case COLD -> mapper.getBackendHealthManager().exceedsCapacity(backendId) 44 | ? Long.MAX_VALUE - 1 // cold backends that exceed safe capacity are put last, just before down ones 45 | : backendStatus.getConnections(); 46 | case STABLE -> backendStatus.getConnections(); 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/core/EndpointKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.core; 21 | 22 | import org.carapaceproxy.utils.StringUtils; 23 | 24 | /** 25 | * Identifier of an endpoint 26 | * 27 | * @author enrico.olivelli 28 | */ 29 | public record EndpointKey(String host, int port) { 30 | 31 | /** 32 | * The minimum port value according to RFC 6335. 33 | */ 34 | public static final int MIN_PORT = 0; 35 | /** 36 | * The maximum port value according to RFC 6335. 37 | */ 38 | public static final int MAX_PORT = 0xffff; 39 | 40 | public EndpointKey { 41 | if (StringUtils.isBlank(host)) { 42 | throw new IllegalArgumentException("Hostname cannot be blank"); 43 | } 44 | if (port > MAX_PORT || port < MIN_PORT) { 45 | throw new IllegalArgumentException("Invalid port: " + port); 46 | } 47 | } 48 | 49 | public static EndpointKey make(String host, int port) { 50 | return new EndpointKey(host, port); 51 | } 52 | 53 | public static EndpointKey make(String hostAndPort) { 54 | int pos = hostAndPort.indexOf(':'); 55 | if (pos <= 0) { 56 | return new EndpointKey(hostAndPort, MIN_PORT); 57 | } 58 | String host = hostAndPort.substring(0, pos); 59 | int port = Integer.parseInt(hostAndPort.substring(pos + 1)); 60 | return new EndpointKey(host, port); 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return host + ":" + port; 66 | } 67 | 68 | public EndpointKey offsetPort(final int offsetPort) { 69 | return make(host(), port() + offsetPort); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/mapper/RandomBackendSelector.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.server.mapper; 2 | 3 | import static org.carapaceproxy.server.config.DirectorConfiguration.ALL_BACKENDS; 4 | import java.security.SecureRandom; 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.SequencedCollection; 10 | import java.util.random.RandomGenerator; 11 | import org.carapaceproxy.server.config.BackendSelector; 12 | import org.carapaceproxy.server.config.DirectorConfiguration; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | /** 17 | * The selector chooses an available backend randomly. 18 | * This means that backend preference is determined by shuffling the resulting list. 19 | * 20 | * @see Collections#shuffle(List, RandomGenerator) 21 | * @see SecureRandom 22 | */ 23 | public class RandomBackendSelector implements BackendSelector { 24 | private static final Logger LOG = LoggerFactory.getLogger(RandomBackendSelector.class); 25 | private static final RandomGenerator RANDOM = new SecureRandom(); 26 | 27 | private final SequencedCollection allBackendIds; 28 | private final Map directors; 29 | 30 | private RandomBackendSelector(final SequencedCollection allBackendIds, final Map directors) { 31 | this.allBackendIds = allBackendIds; 32 | this.directors = directors; 33 | } 34 | 35 | @Override 36 | public List selectBackends(final String userId, final String sessionId, final String director) { 37 | if (!directors.containsKey(director)) { 38 | LOG.error("Director \"{}\" not configured, while handling request userId={} sessionId={}", director, userId, sessionId); 39 | return List.of(); 40 | } 41 | final DirectorConfiguration directorConfig = directors.get(director); 42 | if (directorConfig.getBackends().contains(ALL_BACKENDS)) { 43 | return shuffleCopy(allBackendIds); 44 | } 45 | return shuffleCopy(directorConfig.getBackends()); 46 | } 47 | 48 | public List shuffleCopy(final SequencedCollection ids) { 49 | if (ids.isEmpty()) { 50 | return List.of(); 51 | } 52 | if (ids.size() == 1) { 53 | return List.copyOf(ids); 54 | } 55 | final List result = new ArrayList<>(ids); 56 | Collections.shuffle(result, RANDOM); 57 | return List.copyOf(result); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /.github/workflows/pr-validation.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more contributor 3 | # license agreements. See the NOTICE file distributed with this work for additional 4 | # information regarding copyright ownership. The ASF licenses this file to you 5 | # under the Apache License, Version 2.0 (the # "License"); you may not use this 6 | # file except in compliance with the License. You may obtain a copy of the License 7 | # at: 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software distributed 12 | # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 13 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 14 | # specific language governing permissions and limitations under the License. 15 | # 16 | name: PR Validation 17 | 18 | on: 19 | pull_request: 20 | 21 | env: 22 | MAVEN_OPTS: >- 23 | -Dmaven.artifact.threads=4 24 | -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn 25 | -Dmaven.wagon.httpconnectionManager.ttlSeconds=25 26 | -Dmaven.wagon.http.retryHandler.count=3 27 | MAVEN_CLI_OPTS: >- 28 | --batch-mode 29 | -Pproduction 30 | -Dmaven.test.redirectTestOutputToFile=true 31 | -Dsurefire.rerunFailingTestsCount=3 32 | --update-snapshots 33 | 34 | concurrency: 35 | group: "${{ github.workflow }}-${{ github.ref }}" 36 | cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} 37 | 38 | permissions: 39 | contents: write 40 | 41 | jobs: 42 | build: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v6 46 | - name: 'Set up JDK 21' 47 | uses: actions/setup-java@v5 48 | with: 49 | java-version: '21' 50 | distribution: 'temurin' 51 | cache: 'maven' 52 | - name: 'Build with Maven' 53 | run: ./mvnw -B package -DskipTests --file pom.xml 54 | - uses: actions/upload-artifact@v4 55 | if: always() 56 | with: 57 | name: packages 58 | path: carapace-*/target/carapace-*.jar 59 | retention-days: 7 60 | - name: 'Run validations with Maven' 61 | run: ./mvnw -B test --file pom.xml 62 | - name: 'Submit Dependency Snapshot' 63 | uses: advanced-security/maven-dependency-submission-action@v5 64 | - uses: actions/upload-artifact@v4 65 | if: always() 66 | with: 67 | name: test-results # as expected by pr-validation-report workflow 68 | path: carapace-*/target/surefire-reports/*.xml 69 | retention-days: 1 70 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/filters/RegexpMapUserIdFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.filters; 21 | 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | import org.carapaceproxy.core.ProxyRequest; 25 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 26 | 27 | /** 28 | * Maps a parameter of the querystring to the tenant, using simple pattern matching 29 | * 30 | * @author enrico.olivelli 31 | */ 32 | public class RegexpMapUserIdFilter extends BasicRequestFilter { 33 | 34 | public static final String TYPE = "match-user-regexp"; 35 | 36 | private final String parameterName; 37 | private final Pattern compiledPattern; 38 | 39 | public RegexpMapUserIdFilter(String parameterName, String pattern, RequestMatcher matcher) { 40 | super(matcher); 41 | this.parameterName = parameterName; 42 | this.compiledPattern = Pattern.compile(pattern); 43 | } 44 | 45 | public String getParameterName() { 46 | return parameterName; 47 | } 48 | 49 | public Pattern getCompiledPattern() { 50 | return compiledPattern; 51 | } 52 | 53 | @Override 54 | public void apply(ProxyRequest request) { 55 | if (!checkRequestMatching(request)) { 56 | return; 57 | } 58 | UrlEncodedQueryString queryString = request.getQueryString(); 59 | String value = queryString.get(parameterName); 60 | if (value == null) { 61 | return; 62 | } 63 | Matcher matcher = compiledPattern.matcher(value); 64 | if (!matcher.find()) { 65 | return; 66 | } 67 | if (matcher.groupCount() <= 0) { 68 | return; 69 | } 70 | String group = matcher.group(1); 71 | request.setUserId(group); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/filters/RegexpMapSessionIdFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.filters; 21 | 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | import org.carapaceproxy.core.ProxyRequest; 25 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 26 | 27 | /** 28 | * Maps a parameter of the querystring to the sessionId, using simple pattern matching 29 | * 30 | * @author enrico.olivelli 31 | */ 32 | public class RegexpMapSessionIdFilter extends BasicRequestFilter { 33 | 34 | public static final String TYPE = "match-session-regexp"; 35 | 36 | private final String parameterName; 37 | private final Pattern compiledPattern; 38 | 39 | public RegexpMapSessionIdFilter(String parameterName, String pattern, RequestMatcher matcher) { 40 | super(matcher); 41 | this.parameterName = parameterName; 42 | this.compiledPattern = Pattern.compile(pattern); 43 | } 44 | 45 | public String getParameterName() { 46 | return parameterName; 47 | } 48 | 49 | public Pattern getCompiledPattern() { 50 | return compiledPattern; 51 | } 52 | 53 | @Override 54 | public void apply(ProxyRequest request) { 55 | if (!checkRequestMatching(request)) { 56 | return; 57 | } 58 | UrlEncodedQueryString queryString = request.getQueryString(); 59 | String value = queryString.get(parameterName); 60 | if (value == null) { 61 | return; 62 | } 63 | Matcher _matcher = compiledPattern.matcher(value); 64 | if (!_matcher.find()) { 65 | return; 66 | } 67 | if (_matcher.groupCount() <= 0) { 68 | return; 69 | } 70 | String group = _matcher.group(1); 71 | request.setSessionId(group); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/RouteConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | import org.carapaceproxy.core.ProxyRequest; 23 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 24 | 25 | /** 26 | * Route 27 | */ 28 | public class RouteConfiguration { 29 | 30 | private final String id; 31 | private final boolean enabled; 32 | private final RequestMatcher matcher; 33 | private final String action; 34 | private String errorAction; 35 | private String maintenanceModeAction; 36 | 37 | private String badRequestAction; 38 | 39 | public RouteConfiguration(String id, String action, boolean enabled, RequestMatcher matcher) { 40 | this.id = id; 41 | this.action = action; 42 | this.enabled = enabled; 43 | this.matcher = matcher; 44 | } 45 | 46 | public String getId() { 47 | return id; 48 | } 49 | 50 | public String getAction() { 51 | return action; 52 | } 53 | 54 | public String getErrorAction() { 55 | return errorAction; 56 | } 57 | 58 | public String getMaintenanceModeAction() { 59 | return maintenanceModeAction; 60 | } 61 | 62 | public void setErrorAction(String errorAction) { 63 | this.errorAction = errorAction; 64 | } 65 | 66 | public void setMaintenanceModeAction(String maintenanceModeAction) { 67 | this.maintenanceModeAction = maintenanceModeAction; 68 | } 69 | 70 | public boolean isEnabled() { 71 | return enabled; 72 | } 73 | 74 | public RequestMatcher getMatcher() { 75 | return matcher; 76 | } 77 | 78 | public boolean matches(ProxyRequest request) { 79 | if (!enabled) { 80 | return false; 81 | } 82 | return matcher.matches(request); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/ConnectionPoolConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | import lombok.AllArgsConstructor; 23 | import lombok.Data; 24 | import lombok.NoArgsConstructor; 25 | 26 | /** 27 | * Configuration of a single connection pool 28 | */ 29 | @Data 30 | @NoArgsConstructor 31 | @AllArgsConstructor 32 | public class ConnectionPoolConfiguration { 33 | public static final int DEFAULT_MAX_CONNECTIONS_PER_ENDPOINT = 10; 34 | public static final int DEFAULT_IDLE_TIMEOUT = 60_000; 35 | public static final int DEFAULT_BORROW_TIMEOUT = 60_000; 36 | public static final int DEFAULT_MAX_LIFETIME = 100_000; 37 | public static final int DEFAULT_STUCK_REQUEST_TIMEOUT = 120_000; 38 | public static final int DEFAULT_CONNECT_TIMEOUT = 10_000; 39 | public static final int DEFAULT_DISPOSE_TIMEOUT = 300_000; 40 | public static final int DEFAULT_KEEPALIVE_IDLE = 300; 41 | public static final int DEFAULT_KEEPALIVE_INTERVAL = 60; 42 | public static final int DEFAULT_KEEPALIVE_COUNT = 8; 43 | public static final boolean DEFAULT_KEEPALIVE = true; 44 | 45 | private String id; 46 | private String domain; 47 | private int maxConnectionsPerEndpoint = DEFAULT_MAX_CONNECTIONS_PER_ENDPOINT; 48 | private int borrowTimeout = DEFAULT_BORROW_TIMEOUT; 49 | private int connectTimeout = DEFAULT_CONNECT_TIMEOUT; 50 | private int stuckRequestTimeout = DEFAULT_STUCK_REQUEST_TIMEOUT; 51 | private int idleTimeout = DEFAULT_IDLE_TIMEOUT; 52 | private int maxLifeTime = DEFAULT_MAX_LIFETIME; 53 | private int disposeTimeout = DEFAULT_DISPOSE_TIMEOUT; 54 | private int keepaliveIdle = DEFAULT_KEEPALIVE_IDLE; 55 | private int keepaliveInterval = DEFAULT_KEEPALIVE_INTERVAL; 56 | private int keepaliveCount = DEFAULT_KEEPALIVE_COUNT; 57 | private boolean keepAlive = DEFAULT_KEEPALIVE; 58 | private boolean enabled; 59 | } 60 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/utils/IOUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.utils; 21 | 22 | import java.io.BufferedInputStream; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.InputStreamReader; 26 | import java.io.OutputStream; 27 | import java.io.Reader; 28 | import java.io.StringWriter; 29 | import java.nio.charset.Charset; 30 | import java.nio.charset.StandardCharsets; 31 | 32 | /** 33 | * 34 | * @author francesco.caliumi 35 | */ 36 | public abstract class IOUtils { 37 | 38 | private static final int COPY_BUFFER_SIZE = 64 * 1024; 39 | 40 | public static long copyStreams(InputStream input, OutputStream output) throws IOException { 41 | long count = 0; 42 | int n = 0; 43 | byte[] buffer = new byte[COPY_BUFFER_SIZE]; 44 | while (-1 != (n = input.read(buffer))) { 45 | output.write(buffer, 0, n); 46 | count += n; 47 | } 48 | return count; 49 | } 50 | 51 | /** 52 | * Returns a String encoded in a certain charset from the given InputStream. Do not use this method with stream 53 | * representing files. 54 | * 55 | * @param in 56 | * @param charset 57 | * @return 58 | * @throws IOException 59 | */ 60 | public static String toString(InputStream in, Charset charset) throws IOException { 61 | if (in == null) { 62 | return null; 63 | } 64 | if (charset == null) { 65 | charset = StandardCharsets.UTF_8; 66 | } 67 | try (BufferedInputStream ii = new BufferedInputStream(in); 68 | Reader r = new InputStreamReader(ii, charset)) { 69 | StringWriter writer = new StringWriter(); 70 | int c = r.read(); 71 | while (c != -1) { 72 | writer.write((char) c); 73 | c = r.read(); 74 | } 75 | return writer.toString(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/cluster/impl/NullGroupMembershipHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.cluster.impl; 21 | 22 | import java.util.Collections; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import org.carapaceproxy.cluster.GroupMembershipHandler; 27 | 28 | /** 29 | * Noop implementation 30 | * 31 | * @author eolivelli 32 | */ 33 | public class NullGroupMembershipHandler implements GroupMembershipHandler { 34 | 35 | private Map peerInfo = Map.of("address", "localhost"); 36 | private final String peerId = "local"; 37 | 38 | @Override 39 | public void start() { 40 | } 41 | 42 | @Override 43 | public void watchEvent(String eventId, EventCallback callback) { 44 | // nothing to do, 'cause self events have to be ignored. 45 | } 46 | 47 | @Override 48 | public void fireEvent(String eventId, Map data) { 49 | // nothing to do, 'cause self events have to be ignored. 50 | } 51 | 52 | @Override 53 | public List getPeers() { 54 | return Collections.emptyList(); 55 | } 56 | 57 | @Override 58 | public String describePeer(String id) { 59 | if (peerId.equals(id)) { 60 | return peerId; 61 | } 62 | return null; 63 | } 64 | 65 | @Override 66 | public void stop() { 67 | } 68 | 69 | @Override 70 | public void executeInMutex(String lockId, int timeout, Runnable runnable) { 71 | runnable.run(); 72 | } 73 | 74 | @Override 75 | public String getLocalPeer() { 76 | return peerId; 77 | } 78 | 79 | @Override 80 | public void storeLocalPeerInfo(Map info) { 81 | peerInfo = new HashMap(info); 82 | } 83 | 84 | @Override 85 | public Map loadInfoForPeer(String id) { 86 | if (peerId.equals(id)) { 87 | return peerInfo; 88 | } 89 | return null; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/ListenersResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.util.Map; 23 | import java.util.Set; 24 | import java.util.stream.Collectors; 25 | import javax.servlet.ServletContext; 26 | import javax.ws.rs.GET; 27 | import javax.ws.rs.Path; 28 | import javax.ws.rs.Produces; 29 | import lombok.Data; 30 | import org.carapaceproxy.core.HttpProxyServer; 31 | 32 | /** 33 | * Access to listeners 34 | * 35 | * @author matteo.minardi 36 | */ 37 | @Path("/listeners") 38 | @Produces("application/json") 39 | public class ListenersResource { 40 | 41 | @javax.ws.rs.core.Context 42 | ServletContext context; 43 | 44 | @Data 45 | public static final class ListenerBean { 46 | 47 | private final String host; 48 | private final int port; 49 | private final boolean ssl; 50 | private final String sslCiphers; 51 | private final Set sslProtocols; 52 | private final String defaultCertificate; 53 | private final int totalRequests; 54 | 55 | } 56 | 57 | @GET 58 | public Map getAllListeners() { 59 | final HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 60 | return server.getListeners() 61 | .getListeningChannels() 62 | .values() 63 | .stream() 64 | .collect(Collectors.toMap( 65 | channel -> channel.getHostPort().toString(), 66 | channel -> new ListenerBean( 67 | channel.getHostPort().host(), 68 | channel.getHostPort().port(), 69 | channel.getConfig().ssl(), 70 | channel.getConfig().sslCiphers(), 71 | channel.getConfig().sslProtocols(), 72 | channel.getConfig().defaultCertificate(), 73 | channel.getTotalRequests() 74 | ))); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/server/filters/XTlsProtocolFilterTest.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.server.filters; 2 | 3 | import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; 4 | import static com.github.tomakehurst.wiremock.client.WireMock.absent; 5 | import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; 6 | import static com.github.tomakehurst.wiremock.client.WireMock.get; 7 | import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; 8 | import java.util.Collection; 9 | import java.util.List; 10 | import java.util.Map; 11 | import org.carapaceproxy.server.config.RequestFilterConfiguration; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.junit.runners.Parameterized; 15 | 16 | @RunWith(Parameterized.class) 17 | public class XTlsProtocolFilterTest extends AbstractXTlsFilterTest { 18 | 19 | public XTlsProtocolFilterTest(boolean http1) throws Exception { 20 | super(http1); 21 | } 22 | 23 | @Parameterized.Parameters(name = "Use HTTP/1.x: {0}") 24 | public static Collection data() { 25 | return List.of(new Object[] { true }, new Object[] { false }); 26 | } 27 | 28 | private void setupWireMockForProtocolFilter() { 29 | wireMockRule.stubFor(get(urlEqualTo("/index.html")) 30 | .withHeader("X-Tls-Protocol", equalTo(TLS_PROTOCOL)) 31 | .willReturn(aResponse().withStatus(200).withBody("it works !!"))); 32 | 33 | wireMockRule.stubFor(get(urlEqualTo("/index.html")) 34 | .withHeader("X-Tls-Protocol", absent()) 35 | .willReturn(aResponse().withStatus(200).withBody("it absent !!"))); 36 | } 37 | 38 | @Test 39 | public void testHttpsWithFilter() throws Exception { 40 | setupWireMockForProtocolFilter(); 41 | try (var server = startServer(true, new RequestFilterConfiguration(XTlsProtocolRequestFilter.TYPE, Map.of()) 42 | )) { 43 | assertResponseContains(server.getLocalPort(), true, "it works !!"); 44 | } 45 | } 46 | 47 | @Test 48 | public void testHttpsWithoutFilter() throws Exception { 49 | setupWireMockForProtocolFilter(); 50 | try (var server = startServer(true)) { 51 | assertResponseContains(server.getLocalPort(), true, "it absent !!"); 52 | } 53 | } 54 | 55 | @Test 56 | public void testHttpWithFilter() throws Exception { 57 | setupWireMockForProtocolFilter(); 58 | try (var server = startServer(false, new RequestFilterConfiguration(XTlsProtocolRequestFilter.TYPE, Map.of()))) { 59 | assertResponseContains(server.getLocalPort(), false, "it absent !!"); 60 | } 61 | } 62 | 63 | @Test 64 | public void testHttpWithoutFilter() throws Exception { 65 | setupWireMockForProtocolFilter(); 66 | try (var server = startServer(false)) { 67 | assertResponseContains(server.getLocalPort(), false, "it absent !!"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/CacheResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | import javax.servlet.ServletContext; 26 | import javax.ws.rs.GET; 27 | import javax.ws.rs.Path; 28 | import javax.ws.rs.Produces; 29 | import org.carapaceproxy.core.HttpProxyServer; 30 | import org.carapaceproxy.server.cache.CacheStats; 31 | import org.carapaceproxy.server.cache.ContentsCache; 32 | 33 | /** 34 | * Access to proxy cache 35 | * 36 | * @author enrico.olivelli 37 | */ 38 | @Path("/cache") 39 | @Produces("application/json") 40 | public class CacheResource { 41 | 42 | @javax.ws.rs.core.Context 43 | ServletContext context; 44 | 45 | @Path("/flush") 46 | @GET 47 | public Map flush() { 48 | HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 49 | ContentsCache cache = server.getCache(); 50 | int size = cache.clear(); 51 | Map res = new HashMap<>(); 52 | res.put("result", "ok"); 53 | res.put("cachesize", size); 54 | return res; 55 | } 56 | 57 | @Path("/info") 58 | @GET 59 | public Map info() { 60 | HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 61 | ContentsCache cache = server.getCache(); 62 | int size = cache.getCacheSize(); 63 | CacheStats stats = cache.getStats(); 64 | Map res = new HashMap<>(); 65 | res.put("result", "ok"); 66 | res.put("cachesize", size); 67 | res.put("hits", stats.getHits()); 68 | res.put("misses", stats.getMisses()); 69 | res.put("directMemoryUsed", stats.getDirectMemoryUsed()); 70 | res.put("heapMemoryUsed", stats.getHeapMemoryUsed()); 71 | res.put("totalMemoryUsed", stats.getTotalMemoryUsed()); 72 | return res; 73 | } 74 | 75 | @Path("/inspect") 76 | @GET 77 | public List> inspect() { 78 | HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 79 | ContentsCache cache = server.getCache(); 80 | return cache.inspectCache(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/config/ActionConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.config; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.List; 25 | import lombok.AccessLevel; 26 | import lombok.Data; 27 | import lombok.Setter; 28 | import org.carapaceproxy.server.mapper.CustomHeader; 29 | 30 | /** 31 | * Action 32 | */ 33 | @Data 34 | public class ActionConfiguration { 35 | 36 | /** 37 | * @see org.carapaceproxy.server.mapper.MapResult.Action#PROXY 38 | */ 39 | public static final String TYPE_PROXY = "proxy"; 40 | /** 41 | * @see org.carapaceproxy.server.mapper.MapResult.Action#CACHE 42 | */ 43 | public static final String TYPE_CACHE = "cache"; 44 | /** 45 | * @see org.carapaceproxy.server.mapper.MapResult.Action#STATIC 46 | */ 47 | public static final String TYPE_STATIC = "static"; 48 | /** 49 | * @see org.carapaceproxy.server.mapper.MapResult.Action#ACME_CHALLENGE 50 | */ 51 | public static final String TYPE_ACME_CHALLENGE = "acme-challenge"; 52 | /** 53 | * @see org.carapaceproxy.server.mapper.MapResult.Action#REDIRECT 54 | */ 55 | public static final String TYPE_REDIRECT = "redirect"; 56 | 57 | private final String id; 58 | private final String type; 59 | private final String director; 60 | private final String file; 61 | private final int errorCode; 62 | @Setter(value = AccessLevel.NONE) 63 | private List customHeaders = Collections.emptyList(); // it's a list to keep ordering 64 | private String redirectLocation; 65 | private String redirectProto; 66 | private String redirectHost; 67 | private int redirectPort; 68 | private String redirectPath; 69 | 70 | public ActionConfiguration(String id, String type, String director, String file, int errorcode) { 71 | this.id = id; 72 | this.type = type; 73 | this.director = director; 74 | this.file = file; 75 | this.errorCode = errorcode; 76 | } 77 | 78 | public ActionConfiguration setCustomHeaders(List customHeaders) { 79 | this.customHeaders = Collections.unmodifiableList(customHeaders == null ? new ArrayList<>() : customHeaders); 80 | return this; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/utils/AlpnUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.utils; 21 | 22 | import io.netty.handler.ssl.ApplicationProtocolConfig; 23 | import io.netty.handler.ssl.ApplicationProtocolNames; 24 | import io.netty.handler.ssl.SslContextBuilder; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Set; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | import reactor.netty.http.HttpProtocol; 31 | 32 | /** 33 | * Utility class for ALPN (Application-Layer Protocol Negotiation) configuration. 34 | */ 35 | public class AlpnUtils { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(AlpnUtils.class); 38 | 39 | /** 40 | * Configures ALPN for HTTP/2 support on a server. 41 | * This method checks if HTTP/2 is enabled in the protocols set and configures ALPN accordingly. 42 | * 43 | * @param sslContextBuilder the SslContextBuilder to configure 44 | * @param protocols the set of supported HTTP protocols 45 | * @param host the host name for logging 46 | * @param port the port number for logging 47 | * @return the configured SslContextBuilder 48 | */ 49 | public static SslContextBuilder configureAlpnForServer( 50 | final SslContextBuilder sslContextBuilder, 51 | final Set protocols, 52 | final String host, 53 | final int port 54 | ) { 55 | LOG.debug("Configuring ALPN for HTTP/2 support on listener {}:{}", host, port); 56 | List alpnProtocols = new ArrayList<>(); 57 | if (protocols.contains(HttpProtocol.H2) || protocols.contains(HttpProtocol.H2C)) { 58 | alpnProtocols.add(ApplicationProtocolNames.HTTP_2); 59 | } 60 | if (protocols.contains(HttpProtocol.HTTP11)) { 61 | alpnProtocols.add(ApplicationProtocolNames.HTTP_1_1); 62 | } 63 | 64 | sslContextBuilder.applicationProtocolConfig(new ApplicationProtocolConfig( 65 | ApplicationProtocolConfig.Protocol.ALPN, 66 | ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, 67 | ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, 68 | alpnProtocols 69 | )); 70 | return sslContextBuilder; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/api/HeadersResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import javax.servlet.ServletContext; 25 | import javax.ws.rs.GET; 26 | import javax.ws.rs.Path; 27 | import javax.ws.rs.Produces; 28 | import org.carapaceproxy.core.HttpProxyServer; 29 | import org.carapaceproxy.server.mapper.CustomHeader; 30 | 31 | /** 32 | * Access to configured actions 33 | * 34 | * @author paolo.venturi 35 | */ 36 | @Path("/headers") 37 | @Produces("application/json") 38 | public class HeadersResource { 39 | 40 | @javax.ws.rs.core.Context 41 | ServletContext context; 42 | 43 | public static final class HeaderBean { 44 | 45 | private final String id; 46 | private final String name; 47 | private final String value; 48 | private final String mode; 49 | 50 | public HeaderBean(String id, String name, String value, String mode) { 51 | this.id = id; 52 | this.name = name; 53 | this.value = value; 54 | this.mode = mode; 55 | } 56 | 57 | public String getId() { 58 | return id; 59 | } 60 | 61 | public String getName() { 62 | return name; 63 | } 64 | 65 | public String getValue() { 66 | return value; 67 | } 68 | 69 | public String getMode() { 70 | return mode; 71 | } 72 | } 73 | 74 | @GET 75 | public List getAll() { 76 | final List headers = new ArrayList(); 77 | HttpProxyServer server = (HttpProxyServer) context.getAttribute("server"); 78 | server.getMapper().getHeaders().forEach(header -> { 79 | headers.add(new HeaderBean(header.getId(), header.getName(), header.getValue(), modeToString(header.getMode()))); 80 | }); 81 | 82 | return headers; 83 | } 84 | 85 | static String modeToString(CustomHeader.HeaderMode mode) { 86 | switch (mode) { 87 | case ADD: 88 | return "add"; 89 | case SET: 90 | return "set"; 91 | case REMOVE: 92 | return "remove"; 93 | default: 94 | return "not defined"; 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/utils/RawHttpServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.utils; 21 | 22 | import javax.servlet.http.HttpServlet; 23 | import org.eclipse.jetty.server.Server; 24 | import org.eclipse.jetty.server.ServerConnector; 25 | import org.eclipse.jetty.server.handler.ContextHandlerCollection; 26 | import org.eclipse.jetty.servlet.ServletContextHandler; 27 | import org.eclipse.jetty.servlet.ServletHolder; 28 | 29 | /** 30 | * A Jetty based Http Server 31 | * 32 | * @author enrico.olivelli 33 | */ 34 | public class RawHttpServer implements AutoCloseable { 35 | 36 | private Server httpserver; 37 | private int port = 0; // start ephemeral 38 | private long idleTimeout; // seconds 39 | 40 | private final HttpServlet servlet; 41 | 42 | public RawHttpServer(HttpServlet servlet) { 43 | this.servlet = servlet; 44 | } 45 | 46 | public int start() throws Exception { 47 | stop(); 48 | 49 | httpserver = new Server(port); 50 | 51 | ContextHandlerCollection contexts = new ContextHandlerCollection(); 52 | httpserver.setHandler(contexts); 53 | 54 | ServletContextHandler context = new ServletContextHandler(); 55 | context.setContextPath("/"); 56 | ServletHolder jerseyServlet = new ServletHolder(servlet); 57 | jerseyServlet.setInitOrder(0); 58 | context.addServlet(jerseyServlet, "/"); 59 | contexts.addHandler(context); 60 | httpserver.start(); 61 | 62 | ServerConnector conn = (ServerConnector) httpserver.getConnectors()[0]; 63 | conn.setIdleTimeout(idleTimeout * 1_000); 64 | this.port = conn.getLocalPort(); // keep same port on restart 65 | 66 | return port; 67 | } 68 | 69 | public void restart() throws Exception { 70 | stop(); 71 | start(); 72 | } 73 | 74 | public void stop() throws Exception { 75 | if (httpserver != null) { 76 | httpserver.stop(); 77 | } 78 | httpserver = null; 79 | } 80 | 81 | @Override 82 | public void close() throws Exception { 83 | stop(); 84 | } 85 | 86 | public long getIdleTimeout() { 87 | return idleTimeout; 88 | } 89 | 90 | public void setIdleTimeout(long idleTimeout) { 91 | this.idleTimeout = idleTimeout; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/certificates/ocsp/Digester.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.certificates.ocsp; 21 | 22 | import java.io.OutputStream; 23 | 24 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; 25 | import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; 26 | import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 27 | import org.bouncycastle.cert.ocsp.OCSPReqBuilder; 28 | import org.bouncycastle.crypto.Digest; 29 | import org.bouncycastle.crypto.digests.SHA1Digest; 30 | import org.bouncycastle.crypto.digests.SHA256Digest; 31 | import org.bouncycastle.crypto.io.DigestOutputStream; 32 | import org.bouncycastle.operator.DigestCalculator; 33 | 34 | /** 35 | * BC's {@link OCSPReqBuilder} needs a {@link DigestCalculator} but BC doesn't provide any public implementations of that interface. That's why we need to write our own. There's a default SHA-1 36 | * implementation and one for SHA-256. Which one to use will depend on the Certificate Authority (CA). 37 | */ 38 | public final class Digester implements DigestCalculator { 39 | 40 | public static DigestCalculator sha1() { 41 | Digest digest = new SHA1Digest(); 42 | AlgorithmIdentifier algId = new AlgorithmIdentifier( 43 | OIWObjectIdentifiers.idSHA1); 44 | 45 | return new Digester(digest, algId); 46 | } 47 | 48 | public static DigestCalculator sha256() { 49 | Digest digest = new SHA256Digest(); 50 | 51 | // The OID for SHA-256: http://www.oid-info.com/get/2.16.840.1.101.3.4.2.1 52 | ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier( 53 | "2.16.840.1.101.3.4.2.1").intern(); 54 | AlgorithmIdentifier algId = new AlgorithmIdentifier(oid); 55 | 56 | return new Digester(digest, algId); 57 | } 58 | 59 | private final DigestOutputStream dos; 60 | 61 | private final AlgorithmIdentifier algId; 62 | 63 | private Digester(Digest digest, AlgorithmIdentifier algId) { 64 | this.dos = new DigestOutputStream(digest); 65 | this.algId = algId; 66 | } 67 | 68 | @Override 69 | public AlgorithmIdentifier getAlgorithmIdentifier() { 70 | return algId; 71 | } 72 | 73 | @Override 74 | public OutputStream getOutputStream() { 75 | return dos; 76 | } 77 | 78 | @Override 79 | public byte[] getDigest() { 80 | return dos.getDigest(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/certificates/ocsp/OcspRequestBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.certificates.ocsp; 21 | 22 | import java.io.IOException; 23 | import java.math.BigInteger; 24 | import java.security.cert.CertificateEncodingException; 25 | import java.security.cert.X509Certificate; 26 | 27 | import org.bouncycastle.asn1.DEROctetString; 28 | import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; 29 | import org.bouncycastle.asn1.x509.Extension; 30 | import org.bouncycastle.asn1.x509.Extensions; 31 | import org.bouncycastle.cert.X509CertificateHolder; 32 | import org.bouncycastle.cert.ocsp.CertificateID; 33 | import org.bouncycastle.cert.ocsp.OCSPException; 34 | import org.bouncycastle.cert.ocsp.OCSPReq; 35 | import org.bouncycastle.cert.ocsp.OCSPReqBuilder; 36 | import org.bouncycastle.operator.OperatorCreationException; 37 | import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; 38 | 39 | /** 40 | * This is a simplified version of BC's own {@link OCSPReqBuilder}. 41 | * 42 | * @see OCSPReqBuilder 43 | */ 44 | public class OcspRequestBuilder { 45 | 46 | private X509Certificate certificate; 47 | 48 | private X509Certificate issuer; 49 | 50 | public OcspRequestBuilder certificate(X509Certificate certificate) { 51 | this.certificate = certificate; 52 | return this; 53 | } 54 | 55 | public OcspRequestBuilder issuer(X509Certificate issuer) { 56 | this.issuer = issuer; 57 | return this; 58 | } 59 | 60 | public OCSPReq build() throws OCSPException, OperatorCreationException, CertificateEncodingException, IOException { 61 | // Get certId 62 | CertificateID certId = new CertificateID( 63 | (new BcDigestCalculatorProvider()).get(CertificateID.HASH_SHA1), 64 | new X509CertificateHolder(issuer.getEncoded()), 65 | certificate.getSerialNumber() 66 | ); 67 | OCSPReqBuilder ocspRequestGenerator = new OCSPReqBuilder(); 68 | ocspRequestGenerator.addRequest(certId); 69 | BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis()); 70 | Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(nonce.toByteArray())); 71 | ocspRequestGenerator.setRequestExtensions(new Extensions(new Extension[]{ext})); 72 | 73 | return ocspRequestGenerator.build(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/cache/CacheImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.cache; 21 | 22 | import org.carapaceproxy.server.cache.ContentsCache.ContentKey; 23 | import org.carapaceproxy.server.cache.ContentsCache.CachedContent; 24 | 25 | /** 26 | * 27 | * @author francesco.caliumi 28 | */ 29 | interface CacheImpl { 30 | 31 | /** 32 | * Sets cache to verbose 33 | * @param verbose 34 | */ 35 | public void setVerbose(boolean verbose); 36 | 37 | /** 38 | * Gets the esteemed number entries in cache 39 | * @return 40 | */ 41 | public int getSize(); 42 | 43 | /** 44 | * Gets the esteemed memory consumption of cache contents 45 | * @return 46 | */ 47 | public long getMemSize(); 48 | 49 | /** 50 | * Adds an element to cache and updates the stats 51 | * @param key 52 | * @param payload 53 | */ 54 | public void put(ContentKey key, CachedContent payload); 55 | 56 | /** 57 | * Gets an element from cache if presents and updates the stats 58 | * @param key 59 | * @return Cached element or null if key was not found in cache 60 | */ 61 | public CachedContent get(ContentKey key); 62 | 63 | /** 64 | * Removes an element from cache, frees its resources and updates the stats 65 | * 66 | * NOTE: removal will eventually erase key and payload, so if the cached element has been previously got from the 67 | * cache ti will no longer be usable after calling this method 68 | * 69 | * @param key 70 | * @return The removed element 71 | */ 72 | public void remove(ContentKey key); 73 | 74 | /** 75 | * Tries to force eviction on cache 76 | */ 77 | public void evict(); 78 | 79 | /** 80 | * Removes all elements from cache 81 | * @return Esteemed number of elements removed 82 | */ 83 | public int clear(); 84 | 85 | /** 86 | * Clears the cache and free all its resources 87 | */ 88 | public void close(); 89 | 90 | interface CacheEntriesSink { 91 | public void accept(ContentKey key, CachedContent payload); 92 | } 93 | 94 | /** 95 | * Calls "sink" for every current element in cache 96 | * @param sink 97 | */ 98 | public void inspectCache(CacheEntriesSink sink); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /carapace-server/src/test/java/org/carapaceproxy/api/AuthenticationAPIServerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.api; 21 | 22 | import java.util.List; 23 | import java.util.Properties; 24 | import javax.servlet.http.HttpServletResponse; 25 | import org.carapaceproxy.utils.RawHttpClient; 26 | import org.carapaceproxy.utils.RawHttpClient.BasicAuthCredentials; 27 | import org.carapaceproxy.utils.RawHttpClient.HttpResponse; 28 | import static org.hamcrest.CoreMatchers.containsString; 29 | import static org.hamcrest.MatcherAssert.assertThat; 30 | import static org.junit.Assert.assertFalse; 31 | import static org.junit.Assert.assertTrue; 32 | import org.junit.Test; 33 | 34 | /** 35 | * 36 | * @author enrico.olivelli 37 | */ 38 | public class AuthenticationAPIServerTest extends UseAdminServer { 39 | 40 | @Test 41 | public void testUnauthorizedRequests() throws Exception { 42 | Properties prop = new Properties(HTTP_ADMIN_SERVER_CONFIG); 43 | 44 | prop.put("userrealm.class", "org.carapaceproxy.utils.TestUserRealm"); 45 | prop.put("user.test1", "pass1"); 46 | prop.put("user.test2", "pass2"); 47 | 48 | startServer(prop); 49 | 50 | try (RawHttpClient client = new RawHttpClient("localhost", 8761)) { 51 | BasicAuthCredentials credentials = new BasicAuthCredentials("test1", "pass1"); // valid credentials 52 | HttpResponse resp = client.get("/api/up", credentials); 53 | assertHeaderNotContains(resp, "WWW-Authenticate"); 54 | } 55 | 56 | try (RawHttpClient client = new RawHttpClient("localhost", 8761)) { 57 | BasicAuthCredentials credentials = new BasicAuthCredentials("wrongtest1", "wrongtest1"); // not valid credentials 58 | HttpResponse resp = client.get("/api/up", credentials); 59 | assertHeaderContains(resp, "WWW-Authenticate"); 60 | assertThat(resp.getBodyString(), containsString(HttpServletResponse.SC_UNAUTHORIZED + "")); 61 | } 62 | } 63 | 64 | private void assertHeaderNotContains(HttpResponse resp, String header) { 65 | List lines = resp.getHeaderLines(); 66 | assertFalse(lines.stream().anyMatch(h -> h.contains(header))); 67 | } 68 | 69 | private void assertHeaderContains(HttpResponse resp, String header) { 70 | List lines = resp.getHeaderLines(); 71 | assertTrue(lines.stream().anyMatch(h -> h.contains(header))); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/configstore/ConfigurationStoreUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.configstore; 21 | 22 | import static org.carapaceproxy.utils.CertificatesUtils.createKeystore; 23 | import static org.carapaceproxy.utils.CertificatesUtils.readChainFromKeystore; 24 | import java.security.GeneralSecurityException; 25 | import java.security.Key; 26 | import java.security.KeyFactory; 27 | import java.security.PrivateKey; 28 | import java.security.PublicKey; 29 | import java.security.cert.Certificate; 30 | import java.security.spec.PKCS8EncodedKeySpec; 31 | import java.security.spec.X509EncodedKeySpec; 32 | import java.util.Base64; 33 | 34 | public final class ConfigurationStoreUtils { 35 | 36 | private ConfigurationStoreUtils() { 37 | } 38 | 39 | public static String base64EncodeKey(Key key) { 40 | byte[] data = key.getEncoded(); 41 | if (key instanceof PrivateKey) { // Store Private Key 42 | data = new PKCS8EncodedKeySpec(data).getEncoded(); 43 | } else if (key instanceof PublicKey) { // Store Public Key 44 | data = new X509EncodedKeySpec(data).getEncoded(); 45 | } 46 | return Base64.getEncoder().encodeToString(data); 47 | } 48 | 49 | public static PrivateKey base64DecodePrivateKey(String key) throws GeneralSecurityException { 50 | Base64.Decoder dec = Base64.getDecoder(); 51 | PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(dec.decode(key)); 52 | return KeyFactory.getInstance("RSA").generatePrivate(keySpecPKCS8); 53 | } 54 | 55 | public static PublicKey base64DecodePublicKey(String key) throws GeneralSecurityException { 56 | Base64.Decoder dec = Base64.getDecoder(); 57 | X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(dec.decode(key)); 58 | return KeyFactory.getInstance("RSA").generatePublic(keySpecX509); 59 | } 60 | 61 | public static String base64EncodeCertificateChain(Certificate[] chain, PrivateKey key) throws GeneralSecurityException { 62 | return Base64.getEncoder().encodeToString(createKeystore(chain, key)); 63 | } 64 | 65 | public static Certificate[] base64DecodeCertificateChain(String chain) throws GeneralSecurityException { 66 | if (chain == null || chain.isEmpty()) { 67 | return null; 68 | } 69 | byte[] data = Base64.getDecoder().decode(chain); 70 | return readChainFromKeystore(data); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /carapace-ui/src/main/webapp/src/components/ConnectionPools.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/filters/RequestFilterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to Diennea S.r.l. under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. Diennea S.r.l. licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | */ 20 | package org.carapaceproxy.server.filters; 21 | 22 | import java.util.Map; 23 | import org.carapaceproxy.core.RequestFilter; 24 | import org.carapaceproxy.server.config.ConfigurationNotValidException; 25 | import org.carapaceproxy.server.config.RequestFilterConfiguration; 26 | import org.carapaceproxy.server.mapper.requestmatcher.RequestMatcher; 27 | import org.carapaceproxy.server.mapper.requestmatcher.parser.ParseException; 28 | import org.carapaceproxy.server.mapper.requestmatcher.parser.RequestMatchParser; 29 | 30 | /** 31 | * Factory for all filters. 32 | * 33 | * @author enrico.olivelli 34 | */ 35 | public class RequestFilterFactory { 36 | 37 | @SuppressWarnings("deprecation") 38 | public static RequestFilter buildRequestFilter(RequestFilterConfiguration config) throws ConfigurationNotValidException { 39 | String type = config.getType(); 40 | Map filterConfig = config.getFilterConfig(); 41 | 42 | RequestMatcher matcher; 43 | try { 44 | matcher = new RequestMatchParser(filterConfig.getOrDefault("match", "all").trim()).parse(); 45 | } catch (ParseException e) { 46 | throw new ConfigurationNotValidException(e); 47 | } 48 | switch (type) { 49 | case XForwardedForRequestFilter.TYPE: 50 | return new XForwardedForRequestFilter(matcher); 51 | case XTlsCipherRequestFilter.TYPE: 52 | return new XTlsCipherRequestFilter(matcher); 53 | case XTlsProtocolRequestFilter.TYPE: 54 | return new XTlsProtocolRequestFilter(matcher); 55 | case RegexpMapUserIdFilter.TYPE: { 56 | String param = filterConfig.getOrDefault("param", "userid").trim(); 57 | String regexp = filterConfig.getOrDefault("regexp", "(.*)").trim(); 58 | return new RegexpMapUserIdFilter(param, regexp, matcher); 59 | } 60 | case RegexpMapSessionIdFilter.TYPE: { 61 | String param = filterConfig.getOrDefault("param", "sid").trim(); 62 | String regexp = filterConfig.getOrDefault("regexp", "(.*)").trim(); 63 | return new RegexpMapSessionIdFilter(param, regexp, matcher); 64 | } 65 | default: 66 | throw new ConfigurationNotValidException("bad filter type '" + type 67 | + "' only 'add-x-forwarded-for', 'match-user-regexp'"); 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /carapace-server/src/main/java/org/carapaceproxy/server/cache/CacheByteBufMemoryUsageMetric.java: -------------------------------------------------------------------------------- 1 | package org.carapaceproxy.server.cache; 2 | 3 | import io.netty.buffer.PooledByteBufAllocator; 4 | import io.netty.buffer.UnpooledByteBufAllocator; 5 | import io.prometheus.client.Gauge; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ScheduledExecutorService; 8 | import java.util.concurrent.ScheduledFuture; 9 | import java.util.concurrent.TimeUnit; 10 | import org.carapaceproxy.core.HttpProxyServer; 11 | import org.carapaceproxy.utils.PrometheusUtils; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | public class CacheByteBufMemoryUsageMetric implements Runnable { 16 | 17 | public static final int DEFAULT_PERIOD = 5; // seconds 18 | private static final Logger LOG = LoggerFactory.getLogger(CacheByteBufMemoryUsageMetric.class); 19 | private static final Gauge CACHE_POOLED_BYTEBUF_ALLOCATOR = PrometheusUtils.createGauge("cacheAllocator", 20 | "cache_pooled_allocator_direct_memory_usage", 21 | "Amount of direct memory usage by cache allocator").register(); 22 | private static final Gauge CACHE_UNPOOLED_BYTEBUF_ALLOCATOR = PrometheusUtils.createGauge("cacheAllocator", 23 | "cache_unpooled_allocator_direct_memory_usage", 24 | "Amount of direct memory usage by cache allocator").register(); 25 | private ScheduledExecutorService timer; 26 | private ScheduledFuture scheduledFuture; 27 | private HttpProxyServer parent; 28 | 29 | // can change at runtime 30 | private volatile int period; 31 | private volatile boolean started; // keep track of start() calling 32 | 33 | public CacheByteBufMemoryUsageMetric(HttpProxyServer parent) { 34 | this.period = DEFAULT_PERIOD; 35 | this.parent = parent; 36 | } 37 | 38 | 39 | public int getPeriod() { 40 | return period; 41 | } 42 | 43 | public void setPeriod(int period) { 44 | this.period = period; 45 | } 46 | 47 | public synchronized void start() { 48 | started = true; 49 | if (period <= 0) { 50 | return; 51 | } 52 | if (timer == null) { 53 | timer = Executors.newSingleThreadScheduledExecutor(); 54 | } 55 | LOG.info("Starting cache ByteBufAllocator usage, period: {} seconds", period); 56 | scheduledFuture = timer.scheduleAtFixedRate(this, period, period, TimeUnit.SECONDS); 57 | } 58 | 59 | public synchronized void stop() { 60 | started = false; 61 | if (timer != null) { 62 | timer.shutdown(); 63 | try { 64 | timer.awaitTermination(10, TimeUnit.SECONDS); 65 | timer = null; 66 | scheduledFuture = null; 67 | } catch (InterruptedException err) { 68 | Thread.currentThread().interrupt(); 69 | } 70 | } 71 | } 72 | 73 | 74 | @Override 75 | public void run() { 76 | if (parent.getCachePoolAllocator() instanceof PooledByteBufAllocator) { 77 | CACHE_POOLED_BYTEBUF_ALLOCATOR.set(((PooledByteBufAllocator) parent.getCachePoolAllocator()).metric().usedDirectMemory()); 78 | } else { 79 | CACHE_UNPOOLED_BYTEBUF_ALLOCATOR.set(((UnpooledByteBufAllocator) parent.getCachePoolAllocator()).metric().usedDirectMemory()); 80 | } 81 | } 82 | } 83 | --------------------------------------------------------------------------------