From f3dc999c35bb4342d6d61c2edcd29603164d5fe0 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Wed, 15 Apr 2026 16:22:09 +0300 Subject: [PATCH 01/15] Added support for additional PHP installation paths and improved handling of colon-separated mod directories for RedHat-based systems --- package/rpm/aikido.spec | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/package/rpm/aikido.spec b/package/rpm/aikido.spec index ad4a5f50..66238a0f 100644 --- a/package/rpm/aikido.spec +++ b/package/rpm/aikido.spec @@ -41,7 +41,7 @@ if command -v php -v >/dev/null 2>&1; then fi # Check common PHP installation paths -for php_path in /usr/bin/php* /usr/local/bin/php*; do +for php_path in /usr/bin/php* /usr/local/bin/php* /opt/bin/php*; do if [[ -x "$php_path" && "$php_path" =~ php([0-9]+\.[0-9]+)$ ]]; then version=$("$php_path" -v | grep -oP 'PHP \K\d+\.\d+' | head -n 1) if [[ ! " ${PHP_VERSIONS[@]} " =~ " ${version} " ]]; then @@ -133,9 +133,10 @@ for PHP_VERSION in "${PHP_VERSIONS[@]}"; do fi else # RedHat-based system - if [ -d "$PHP_MOD_DIR" ]; then - echo "Installing new Aikido mod in $PHP_MOD_DIR/zz-aikido-%{version}.ini..." - ln -sf /opt/aikido-%{version}/aikido.ini $PHP_MOD_DIR/zz-aikido-%{version}.ini + PHP_MOD_DIR_FIRST="${PHP_MOD_DIR%%:*}" + if [ -d "$PHP_MOD_DIR_FIRST" ]; then + echo "Installing new Aikido mod in $PHP_MOD_DIR_FIRST/zz-aikido-%{version}.ini..." + ln -sf /opt/aikido-%{version}/aikido.ini $PHP_MOD_DIR_FIRST/zz-aikido-%{version}.ini else echo "No mod dir for PHP $PHP_VERSION! Skipping..." continue @@ -198,7 +199,7 @@ if command -v php -v >/dev/null 2>&1; then fi # Check common PHP installation paths -for php_path in /usr/bin/php* /usr/local/bin/php*; do +for php_path in /usr/bin/php* /usr/local/bin/php* /opt/bin/php*; do if [[ -x "$php_path" && "$php_path" =~ php([0-9]+\.[0-9]+)$ ]]; then version=$("$php_path" -v | grep -oP 'PHP \K\d+\.\d+' | head -n 1) if [[ ! " ${PHP_VERSIONS[@]} " =~ " ${version} " ]]; then @@ -252,10 +253,11 @@ for PHP_VERSION in "${PHP_VERSIONS[@]}"; do rm -f $PHP_DEBIAN_MOD_DIR_APACHE2/zz-aikido-%{version}.ini fi else - # RedHat-based system - if [ -d "$PHP_MOD_DIR" ]; then - echo "Uninstalling Aikido mod from $PHP_MOD_DIR/zz-aikido-%{version}.ini..." - rm -f $PHP_MOD_DIR/zz-aikido-%{version}.ini + # RedHat-based system (take first dir if colon-separated) + PHP_MOD_DIR_FIRST="${PHP_MOD_DIR%%:*}" + if [ -f "$PHP_MOD_DIR_FIRST/zz-aikido-%{version}.ini" ]; then + echo "Uninstalling Aikido mod from $PHP_MOD_DIR_FIRST/zz-aikido-%{version}.ini..." + rm -f $PHP_MOD_DIR_FIRST/zz-aikido-%{version}.ini fi fi From b8dfdc6f481aee4fab8f0e7c0783868d3fa1d199 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Wed, 15 Apr 2026 18:25:42 +0300 Subject: [PATCH 02/15] Add workflow to build Lambda layers for multiple PHP versions and update release process to include Lambda layer artifacts --- .github/workflows/build.yml | 67 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 9 ++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 72b131b9..1e38e9ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -310,6 +310,73 @@ jobs: path: | ~/rpmbuild/RPMS/${{ env.ARCH }}/${{ env.AIKIDO_ARTIFACT_RELEASE }} + build_lambda_layers: + name: Build Lambda layers php-${{ matrix.php_version }} + runs-on: ubuntu-24.04 + container: + image: quay.io/centos/centos:stream9 + needs: [ build_libs, build_php_extension_nts ] + strategy: + matrix: + php_version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get Aikido version + run: | + AIKIDO_VERSION=$(grep '#define PHP_AIKIDO_VERSION' lib/php-extension/include/php_aikido.h | awk -F'"' '{print $2}') + echo "AIKIDO_VERSION=$AIKIDO_VERSION" >> $GITHUB_ENV + + - name: Download NTS extension + uses: actions/download-artifact@v4 + with: + name: aikido-extension-php-${{ matrix.php_version }}-nts-x86_64 + + - name: Download agent + uses: actions/download-artifact@v4 + with: + name: aikido-agent-x86_64 + + - name: Download request processor + uses: actions/download-artifact@v4 + with: + name: aikido-request-processor-x86_64 + + - name: Download Aikido Zen Internals Lib + run: | + AIKIDO_LIBZEN_VERSION=0.1.60 + curl -L -O https://github.com/AikidoSec/zen-internals/releases/download/v${AIKIDO_LIBZEN_VERSION}/libzen_internals_x86_64-unknown-linux-gnu.so + + - name: Build Lambda layer zip + run: | + LAYER_DIR=lambda-layer + AIKIDO_DIR=$LAYER_DIR/opt/aikido-${{ env.AIKIDO_VERSION }} + BREF_CONF_DIR=$LAYER_DIR/opt/bref/etc/php/conf.d + + mkdir -p $AIKIDO_DIR $BREF_CONF_DIR + + cp aikido-extension-php-${{ matrix.php_version }}-nts.so $AIKIDO_DIR/ + cp aikido-agent $AIKIDO_DIR/ + cp aikido-request-processor.so $AIKIDO_DIR/ + cp libzen_internals_x86_64-unknown-linux-gnu.so $AIKIDO_DIR/ + chmod +x $AIKIDO_DIR/aikido-agent + + echo "extension=/opt/aikido-${{ env.AIKIDO_VERSION }}/aikido-extension-php-${{ matrix.php_version }}-nts.so" > $BREF_CONF_DIR/ext-aikido.ini + + cd $LAYER_DIR + zip -r ../aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip opt/ + + - name: Archive Lambda layer + uses: actions/upload-artifact@v4 + with: + name: aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip + if-no-files-found: error + path: | + aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip + build_deb: name: Build deb ${{ matrix.arch == '' && 'x86_64' || 'arm' }} runs-on: ubuntu-24.04${{ matrix.arch }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8183318..ecb69b2e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,10 +26,16 @@ jobs: with: pattern: | aikido-php-firewall* + + - name: Download Lambda layer artifacts + uses: actions/download-artifact@v4 + with: + pattern: | + aikido-firewall-bref-lambda-layer-* - name: List Artifacts run: | - ls -l + ls -lR pwd - name: Deploy to GitHub Release (draft) @@ -38,3 +44,4 @@ jobs: draft: true files: | ./**/aikido-php-firewall* + ./**/aikido-firewall-bref-lambda-layer-* From 396bd461d7f48ab8817debc44901bd9853419257 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Thu, 16 Apr 2026 13:39:35 +0300 Subject: [PATCH 03/15] Refactor Lambda layer build process to utilize artifact paths for extensions and agents --- .github/workflows/build.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e38e9ea..b2a45d7a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -313,8 +313,6 @@ jobs: build_lambda_layers: name: Build Lambda layers php-${{ matrix.php_version }} runs-on: ubuntu-24.04 - container: - image: quay.io/centos/centos:stream9 needs: [ build_libs, build_php_extension_nts ] strategy: matrix: @@ -329,26 +327,29 @@ jobs: run: | AIKIDO_VERSION=$(grep '#define PHP_AIKIDO_VERSION' lib/php-extension/include/php_aikido.h | awk -F'"' '{print $2}') echo "AIKIDO_VERSION=$AIKIDO_VERSION" >> $GITHUB_ENV + echo "AIKIDO_LIBZEN_VERSION=0.1.60" >> $GITHUB_ENV - name: Download NTS extension uses: actions/download-artifact@v4 with: name: aikido-extension-php-${{ matrix.php_version }}-nts-x86_64 + path: artifacts/extension - name: Download agent uses: actions/download-artifact@v4 with: name: aikido-agent-x86_64 + path: artifacts/agent - name: Download request processor uses: actions/download-artifact@v4 with: name: aikido-request-processor-x86_64 + path: artifacts/request-processor - name: Download Aikido Zen Internals Lib run: | - AIKIDO_LIBZEN_VERSION=0.1.60 - curl -L -O https://github.com/AikidoSec/zen-internals/releases/download/v${AIKIDO_LIBZEN_VERSION}/libzen_internals_x86_64-unknown-linux-gnu.so + curl -L -O https://github.com/AikidoSec/zen-internals/releases/download/v${{ env.AIKIDO_LIBZEN_VERSION }}/libzen_internals_x86_64-unknown-linux-gnu.so - name: Build Lambda layer zip run: | @@ -358,14 +359,16 @@ jobs: mkdir -p $AIKIDO_DIR $BREF_CONF_DIR - cp aikido-extension-php-${{ matrix.php_version }}-nts.so $AIKIDO_DIR/ - cp aikido-agent $AIKIDO_DIR/ - cp aikido-request-processor.so $AIKIDO_DIR/ + find artifacts/extension -name "aikido-extension-php-${{ matrix.php_version }}-nts.so" -exec cp {} $AIKIDO_DIR/ \; + find artifacts/agent -name "aikido-agent" -exec cp {} $AIKIDO_DIR/ \; + find artifacts/request-processor -name "aikido-request-processor.so" -exec cp {} $AIKIDO_DIR/ \; cp libzen_internals_x86_64-unknown-linux-gnu.so $AIKIDO_DIR/ chmod +x $AIKIDO_DIR/aikido-agent echo "extension=/opt/aikido-${{ env.AIKIDO_VERSION }}/aikido-extension-php-${{ matrix.php_version }}-nts.so" > $BREF_CONF_DIR/ext-aikido.ini + ls -la $AIKIDO_DIR/ + cd $LAYER_DIR zip -r ../aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip opt/ From b7bd5881681a91b3b1cf73cdb0c8f3a595b37646 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Thu, 16 Apr 2026 13:58:33 +0300 Subject: [PATCH 04/15] Refactor socket and PID path initialization to use dynamic runtime directory based on AWS Lambda environment --- lib/agent/constants/constants.go | 19 ++++++++++++++++--- lib/php-extension/Agent.cpp | 13 +++++++++++-- lib/request-processor/globals/globals.go | 15 +++++++++++---- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/agent/constants/constants.go b/lib/agent/constants/constants.go index c787bf3d..e4045cf9 100644 --- a/lib/agent/constants/constants.go +++ b/lib/agent/constants/constants.go @@ -1,9 +1,22 @@ package constants +import "os" + +const Version = "1.5.4" + +var SocketPath string +var PidPath string + +func init() { + runDir := "/run/aikido-" + Version + if _, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME"); ok { + runDir = "/tmp/aikido-" + Version + } + SocketPath = runDir + "/aikido-agent.sock" + PidPath = runDir + "/aikido-agent.pid" +} + const ( - Version = "1.5.4" - SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" - PidPath = "/run/aikido-" + Version + "/aikido-agent.pid" ConfigUpdatedAtMethod = "GET" ConfigUpdatedAtAPI = "/config" ConfigAPIMethod = "GET" diff --git a/lib/php-extension/Agent.cpp b/lib/php-extension/Agent.cpp index 7ef0ee0a..bd834dfa 100644 --- a/lib/php-extension/Agent.cpp +++ b/lib/php-extension/Agent.cpp @@ -1,5 +1,13 @@ #include "Includes.h" +static std::string GetRuntimeDir() { + const char* lambdaEnv = getenv("AWS_LAMBDA_FUNCTION_NAME"); + if (lambdaEnv != nullptr) { + return "/tmp/aikido-" + std::string(PHP_AIKIDO_VERSION); + } + return "/run/aikido-" + std::string(PHP_AIKIDO_VERSION); +} + vector Agent::GetPIDsFromRunningProcesses(const std::string& aikidoAgentPath) { vector agentPIDs; @@ -105,7 +113,7 @@ bool Agent::IsRunning(const std::string& aikidoAgentPath, const std::string& aik AIKIDO_LOG_INFO("Found socket file \"%s\" on disk! Checking if Aikido Agent process is running...\n", aikidoAgentSocketPath.c_str()); - std::string aikidoAgentPidPath = "/run/aikido-" + std::string(PHP_AIKIDO_VERSION) + "/aikido-agent.pid"; + std::string aikidoAgentPidPath = GetRuntimeDir() + "/aikido-agent.pid"; pid_t agentPIDFromFile = this->GetPIDFromFile(aikidoAgentPidPath); vector agentPIDsFromRunningProcesses = this->GetPIDsFromRunningProcesses(aikidoAgentPath); if (agentPIDFromFile == -1 || @@ -126,7 +134,8 @@ bool Agent::IsRunning(const std::string& aikidoAgentPath, const std::string& aik bool Agent::Init() { std::string aikidoAgentPath = "/opt/aikido-" + std::string(PHP_AIKIDO_VERSION) + "/aikido-agent"; - std::string aikidoAgentSocketPath = "/run/aikido-" + std::string(PHP_AIKIDO_VERSION) + "/aikido-agent.sock"; + std::string runtimeDir = GetRuntimeDir(); + std::string aikidoAgentSocketPath = runtimeDir + "/aikido-agent.sock"; if (this->IsRunning(aikidoAgentPath, aikidoAgentSocketPath)) { AIKIDO_LOG_INFO("Aikido Agent is already running! Skipping init...\n"); diff --git a/lib/request-processor/globals/globals.go b/lib/request-processor/globals/globals.go index 10eb4d96..01386fde 100644 --- a/lib/request-processor/globals/globals.go +++ b/lib/request-processor/globals/globals.go @@ -78,7 +78,14 @@ func CreateServer(token string) *ServerData { return Servers[token] } -const ( - Version = "1.5.4" - SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" -) +const Version = "1.5.4" + +var SocketPath string + +func init() { + runDir := "/run/aikido-" + Version + if _, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME"); ok { + runDir = "/tmp/aikido-" + Version + } + SocketPath = runDir + "/aikido-agent.sock" +} From 01ef84c0aa9afdc019f8b1cf7861dff2046e2f38 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Thu, 16 Apr 2026 16:21:37 +0300 Subject: [PATCH 05/15] Update Lambda layer build process to correct directory structure and artifact naming conventions for Bref --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2a45d7a..546c631a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -354,8 +354,8 @@ jobs: - name: Build Lambda layer zip run: | LAYER_DIR=lambda-layer - AIKIDO_DIR=$LAYER_DIR/opt/aikido-${{ env.AIKIDO_VERSION }} - BREF_CONF_DIR=$LAYER_DIR/opt/bref/etc/php/conf.d + AIKIDO_DIR=$LAYER_DIR/aikido-${{ env.AIKIDO_VERSION }} + BREF_CONF_DIR=$LAYER_DIR/bref/etc/php/conf.d mkdir -p $AIKIDO_DIR $BREF_CONF_DIR @@ -370,12 +370,12 @@ jobs: ls -la $AIKIDO_DIR/ cd $LAYER_DIR - zip -r ../aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip opt/ + zip -r ../aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip . - name: Archive Lambda layer uses: actions/upload-artifact@v4 with: - name: aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip + name: aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64 if-no-files-found: error path: | aikido-firewall-bref-lambda-layer-php-${{ matrix.php_version }}-x86_64.zip From af20ddaba220a9cd84182591d2879a87a3d11bb3 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Thu, 16 Apr 2026 17:03:53 +0300 Subject: [PATCH 06/15] debug --- lib/agent/constants/constants.go | 13 +++++++++++-- lib/php-extension/Agent.cpp | 3 +++ lib/request-processor/globals/globals.go | 9 ++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/agent/constants/constants.go b/lib/agent/constants/constants.go index e4045cf9..5dba2c97 100644 --- a/lib/agent/constants/constants.go +++ b/lib/agent/constants/constants.go @@ -1,6 +1,9 @@ package constants -import "os" +import ( + "fmt" + "os" +) const Version = "1.5.4" @@ -8,12 +11,18 @@ var SocketPath string var PidPath string func init() { + lambdaName, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME") + fmt.Printf("[aikido-agent] init: AWS_LAMBDA_FUNCTION_NAME=%q, found=%v\n", lambdaName, ok) runDir := "/run/aikido-" + Version - if _, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME"); ok { + if ok { runDir = "/tmp/aikido-" + Version + fmt.Printf("[aikido-agent] init: Using /tmp path for Lambda\n") + } else { + fmt.Printf("[aikido-agent] init: Using /run path (non-Lambda)\n") } SocketPath = runDir + "/aikido-agent.sock" PidPath = runDir + "/aikido-agent.pid" + fmt.Printf("[aikido-agent] init: SocketPath=%s, PidPath=%s\n", SocketPath, PidPath) } const ( diff --git a/lib/php-extension/Agent.cpp b/lib/php-extension/Agent.cpp index bd834dfa..17c9e4f8 100644 --- a/lib/php-extension/Agent.cpp +++ b/lib/php-extension/Agent.cpp @@ -2,9 +2,12 @@ static std::string GetRuntimeDir() { const char* lambdaEnv = getenv("AWS_LAMBDA_FUNCTION_NAME"); + AIKIDO_LOG_INFO("GetRuntimeDir: AWS_LAMBDA_FUNCTION_NAME=%s\n", lambdaEnv ? lambdaEnv : "(null)"); if (lambdaEnv != nullptr) { + AIKIDO_LOG_INFO("GetRuntimeDir: Using /tmp path for Lambda\n"); return "/tmp/aikido-" + std::string(PHP_AIKIDO_VERSION); } + AIKIDO_LOG_INFO("GetRuntimeDir: Using /run path (non-Lambda)\n"); return "/run/aikido-" + std::string(PHP_AIKIDO_VERSION); } diff --git a/lib/request-processor/globals/globals.go b/lib/request-processor/globals/globals.go index 01386fde..0af38fe9 100644 --- a/lib/request-processor/globals/globals.go +++ b/lib/request-processor/globals/globals.go @@ -1,6 +1,7 @@ package globals import ( + "fmt" "log" "os" "regexp" @@ -83,9 +84,15 @@ const Version = "1.5.4" var SocketPath string func init() { + lambdaName, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME") + fmt.Printf("[aikido-request-processor] init: AWS_LAMBDA_FUNCTION_NAME=%q, found=%v\n", lambdaName, ok) runDir := "/run/aikido-" + Version - if _, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME"); ok { + if ok { runDir = "/tmp/aikido-" + Version + fmt.Printf("[aikido-request-processor] init: Using /tmp path for Lambda\n") + } else { + fmt.Printf("[aikido-request-processor] init: Using /run path (non-Lambda)\n") } SocketPath = runDir + "/aikido-agent.sock" + fmt.Printf("[aikido-request-processor] init: SocketPath=%s\n", SocketPath) } From c520bd4dbed0259ac6263176d1c09f40a09b08c1 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Thu, 16 Apr 2026 17:32:02 +0300 Subject: [PATCH 07/15] Refactor runtime directory initialization to check for writable run directory, adjusting paths for AWS Lambda environment accordingly --- lib/agent/constants/constants.go | 17 ++++++----------- lib/php-extension/Agent.cpp | 6 +----- lib/request-processor/globals/globals.go | 13 +++++-------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/agent/constants/constants.go b/lib/agent/constants/constants.go index 5dba2c97..3ba62aff 100644 --- a/lib/agent/constants/constants.go +++ b/lib/agent/constants/constants.go @@ -1,28 +1,23 @@ package constants -import ( - "fmt" - "os" -) +import "os" const Version = "1.5.4" var SocketPath string var PidPath string +func isRunDirWritable() bool { + return os.MkdirAll("/run/aikido-writetest", 0777) == nil && os.Remove("/run/aikido-writetest") == nil +} + func init() { - lambdaName, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME") - fmt.Printf("[aikido-agent] init: AWS_LAMBDA_FUNCTION_NAME=%q, found=%v\n", lambdaName, ok) runDir := "/run/aikido-" + Version - if ok { + if !isRunDirWritable() { runDir = "/tmp/aikido-" + Version - fmt.Printf("[aikido-agent] init: Using /tmp path for Lambda\n") - } else { - fmt.Printf("[aikido-agent] init: Using /run path (non-Lambda)\n") } SocketPath = runDir + "/aikido-agent.sock" PidPath = runDir + "/aikido-agent.pid" - fmt.Printf("[aikido-agent] init: SocketPath=%s, PidPath=%s\n", SocketPath, PidPath) } const ( diff --git a/lib/php-extension/Agent.cpp b/lib/php-extension/Agent.cpp index 17c9e4f8..6d0d68ef 100644 --- a/lib/php-extension/Agent.cpp +++ b/lib/php-extension/Agent.cpp @@ -1,13 +1,9 @@ #include "Includes.h" static std::string GetRuntimeDir() { - const char* lambdaEnv = getenv("AWS_LAMBDA_FUNCTION_NAME"); - AIKIDO_LOG_INFO("GetRuntimeDir: AWS_LAMBDA_FUNCTION_NAME=%s\n", lambdaEnv ? lambdaEnv : "(null)"); - if (lambdaEnv != nullptr) { - AIKIDO_LOG_INFO("GetRuntimeDir: Using /tmp path for Lambda\n"); + if (getenv("AWS_LAMBDA_FUNCTION_NAME") != nullptr) { return "/tmp/aikido-" + std::string(PHP_AIKIDO_VERSION); } - AIKIDO_LOG_INFO("GetRuntimeDir: Using /run path (non-Lambda)\n"); return "/run/aikido-" + std::string(PHP_AIKIDO_VERSION); } diff --git a/lib/request-processor/globals/globals.go b/lib/request-processor/globals/globals.go index 0af38fe9..e7edeb09 100644 --- a/lib/request-processor/globals/globals.go +++ b/lib/request-processor/globals/globals.go @@ -1,7 +1,6 @@ package globals import ( - "fmt" "log" "os" "regexp" @@ -83,16 +82,14 @@ const Version = "1.5.4" var SocketPath string +func isRunDirWritable() bool { + return os.MkdirAll("/run/aikido-writetest", 0777) == nil && os.Remove("/run/aikido-writetest") == nil +} + func init() { - lambdaName, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME") - fmt.Printf("[aikido-request-processor] init: AWS_LAMBDA_FUNCTION_NAME=%q, found=%v\n", lambdaName, ok) runDir := "/run/aikido-" + Version - if ok { + if !isRunDirWritable() { runDir = "/tmp/aikido-" + Version - fmt.Printf("[aikido-request-processor] init: Using /tmp path for Lambda\n") - } else { - fmt.Printf("[aikido-request-processor] init: Using /run path (non-Lambda)\n") } SocketPath = runDir + "/aikido-agent.sock" - fmt.Printf("[aikido-request-processor] init: SocketPath=%s\n", SocketPath) } From 4cf396a88d6052187863d8c302efd4eedc1121ce Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Thu, 16 Apr 2026 17:38:31 +0300 Subject: [PATCH 08/15] Update isRunDirWritable function to use os.WriteFile for checking writable run directory in agent and request-processor modules --- lib/agent/constants/constants.go | 2 +- lib/request-processor/globals/globals.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/agent/constants/constants.go b/lib/agent/constants/constants.go index 3ba62aff..f0426f61 100644 --- a/lib/agent/constants/constants.go +++ b/lib/agent/constants/constants.go @@ -8,7 +8,7 @@ var SocketPath string var PidPath string func isRunDirWritable() bool { - return os.MkdirAll("/run/aikido-writetest", 0777) == nil && os.Remove("/run/aikido-writetest") == nil + return os.WriteFile("/run/aikido-probe", nil, 0644) == nil && os.Remove("/run/aikido-probe") == nil } func init() { diff --git a/lib/request-processor/globals/globals.go b/lib/request-processor/globals/globals.go index e7edeb09..418cd1d1 100644 --- a/lib/request-processor/globals/globals.go +++ b/lib/request-processor/globals/globals.go @@ -83,7 +83,7 @@ const Version = "1.5.4" var SocketPath string func isRunDirWritable() bool { - return os.MkdirAll("/run/aikido-writetest", 0777) == nil && os.Remove("/run/aikido-writetest") == nil + return os.WriteFile("/run/aikido-probe", nil, 0644) == nil && os.Remove("/run/aikido-probe") == nil } func init() { From d5c523c321ba29d9d24e12adf434357c823f9d50 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Thu, 16 Apr 2026 18:24:25 +0300 Subject: [PATCH 09/15] test --- lib/php-extension/Agent.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/php-extension/Agent.cpp b/lib/php-extension/Agent.cpp index 6d0d68ef..68b35777 100644 --- a/lib/php-extension/Agent.cpp +++ b/lib/php-extension/Agent.cpp @@ -148,6 +148,17 @@ bool Agent::Init() { return false; } + // Wait for the agent to bind its Unix socket (max ~1s) so the first + // request doesn't race against agent startup. This matters on Lambda + // cold starts where MINIT and the first invoke happen back-to-back. + for (int i = 0; i < 200; i++) { + if (FileExists(aikidoAgentSocketPath)) { + AIKIDO_LOG_INFO("Aikido Agent socket ready after %d ms\n", i * 5); + return true; + } + usleep(5000); + } + AIKIDO_LOG_WARN("Aikido Agent socket did not appear within 1s\n"); return true; } From 7399dc865e3e10d2661f33ddfcbb9dee8aaea914 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Fri, 17 Apr 2026 14:26:54 +0300 Subject: [PATCH 10/15] Pass IsLambda to go modules --- lib/agent/constants/constants.go | 26 +++++++------- lib/agent/main.go | 15 ++++++++ lib/php-extension/Agent.cpp | 37 ++++++++++++-------- lib/php-extension/RequestProcessor.cpp | 2 +- lib/php-extension/Utils.cpp | 4 +++ lib/php-extension/include/RequestProcessor.h | 2 +- lib/php-extension/include/Utils.h | 2 ++ lib/request-processor/globals/globals.go | 18 +++++----- lib/request-processor/main.go | 4 ++- 9 files changed, 68 insertions(+), 42 deletions(-) diff --git a/lib/agent/constants/constants.go b/lib/agent/constants/constants.go index f0426f61..97dd1563 100644 --- a/lib/agent/constants/constants.go +++ b/lib/agent/constants/constants.go @@ -1,23 +1,21 @@ package constants -import "os" - const Version = "1.5.4" -var SocketPath string -var PidPath string - -func isRunDirWritable() bool { - return os.WriteFile("/run/aikido-probe", nil, 0644) == nil && os.Remove("/run/aikido-probe") == nil -} +var ( + IsLambda bool + SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" + PidPath = "/run/aikido-" + Version + "/aikido-agent.pid" +) -func init() { - runDir := "/run/aikido-" + Version - if !isRunDirWritable() { - runDir = "/tmp/aikido-" + Version +// SetRuntimeDir switches the socket/pid directory to /tmp when running on +// Lambda. The flag is passed from C++ via an argv (--lambda) when the agent +// is spawned. +func SetRuntimeDir(isLambda bool) { + if isLambda { + SocketPath = "/tmp/aikido-" + "/aikido-agent.sock" + PidPath = "/tmp/aikido-" + "/aikido-agent.pid" } - SocketPath = runDir + "/aikido-agent.sock" - PidPath = runDir + "/aikido-agent.pid" } const ( diff --git a/lib/agent/main.go b/lib/agent/main.go index 1198bf0e..71055a18 100644 --- a/lib/agent/main.go +++ b/lib/agent/main.go @@ -24,6 +24,12 @@ var serversCleanupChannel = make(chan struct{}) var serversCleanupTicker = time.NewTicker(time.Minute) func serversCleanupRoutine(_ *ServerData) { + // On Lambda, the agent process is frozen between invocations while + // wall-clock time keeps passing. Inactivity-based cleanup wrongly + // evicts the registered server and breaks subsequent requests. + if constants.IsLambda { + return + } for _, serverKey := range globals.GetServersKeys() { server := globals.GetServer(serverKey) if server == nil { @@ -88,6 +94,15 @@ func AgentUninit() { } func main() { + isLambda := false + for _, arg := range os.Args[1:] { + if arg == "--lambda" { + isLambda = true + break + } + } + constants.SetRuntimeDir(isLambda) + if !AgentInit() { log.Errorf(log.MainLogger, "Agent initialization failed!") os.Exit(-2) diff --git a/lib/php-extension/Agent.cpp b/lib/php-extension/Agent.cpp index 68b35777..9b8d7ef1 100644 --- a/lib/php-extension/Agent.cpp +++ b/lib/php-extension/Agent.cpp @@ -1,7 +1,7 @@ #include "Includes.h" static std::string GetRuntimeDir() { - if (getenv("AWS_LAMBDA_FUNCTION_NAME") != nullptr) { + if (IsLambda()) { return "/tmp/aikido-" + std::string(PHP_AIKIDO_VERSION); } return "/run/aikido-" + std::string(PHP_AIKIDO_VERSION); @@ -45,13 +45,16 @@ bool Agent::Start(std::string aikidoAgentPath) { posix_spawnattr_t attr; posix_spawnattr_init(&attr); - char* argv[] = { - const_cast(aikidoAgentPath.c_str()), - nullptr - }; + char lambdaFlag[] = "--lambda"; + std::vector argvVec; + argvVec.push_back(const_cast(aikidoAgentPath.c_str())); + if (IsLambda()) { + argvVec.push_back(lambdaFlag); + } + argvVec.push_back(nullptr); pid_t agentPid; - int status = posix_spawn(&agentPid, aikidoAgentPath.c_str(), nullptr, &attr, argv, nullptr); + int status = posix_spawn(&agentPid, aikidoAgentPath.c_str(), nullptr, &attr, argvVec.data(), nullptr); posix_spawnattr_destroy(&attr); if (status != 0) { AIKIDO_LOG_ERROR("Failed to start Aikido Agent process: %s\n", strerror(status)); @@ -148,17 +151,21 @@ bool Agent::Init() { return false; } - // Wait for the agent to bind its Unix socket (max ~1s) so the first - // request doesn't race against agent startup. This matters on Lambda - // cold starts where MINIT and the first invoke happen back-to-back. - for (int i = 0; i < 200; i++) { - if (FileExists(aikidoAgentSocketPath)) { - AIKIDO_LOG_INFO("Aikido Agent socket ready after %d ms\n", i * 5); - return true; + // On Lambda cold starts, MINIT and the first invoke happen back-to-back, + // so the first gRPC call can race against agent startup. Block here + // (up to ~1s) until the agent has bound its Unix socket. On regular + // long-running SAPIs (php-fpm, apache, frankenphp) MINIT runs well + // before any request, so this wait is unnecessary. + if (IsLambda()) { + for (int i = 0; i < 200; i++) { + if (FileExists(aikidoAgentSocketPath)) { + AIKIDO_LOG_INFO("Aikido Agent socket ready after %d ms\n", i * 5); + return true; + } + usleep(5000); } - usleep(5000); + AIKIDO_LOG_WARN("Aikido Agent socket did not appear within 1s\n"); } - AIKIDO_LOG_WARN("Aikido Agent socket did not appear within 1s\n"); return true; } diff --git a/lib/php-extension/RequestProcessor.cpp b/lib/php-extension/RequestProcessor.cpp index c728363f..fbc9a715 100644 --- a/lib/php-extension/RequestProcessor.cpp +++ b/lib/php-extension/RequestProcessor.cpp @@ -62,7 +62,7 @@ bool RequestProcessor::Init() { return false; } - if (!requestProcessorInitFn(GoCreateString(AIKIDO_GLOBAL(sapi_name)))) { + if (!requestProcessorInitFn(GoCreateString(AIKIDO_GLOBAL(sapi_name)), IsLambda())) { AIKIDO_LOG_ERROR("Failed to initialize Aikido Request Processor!\n"); this->initFailed = true; return false; diff --git a/lib/php-extension/Utils.cpp b/lib/php-extension/Utils.cpp index 1ad4b29c..c879bc0c 100644 --- a/lib/php-extension/Utils.cpp +++ b/lib/php-extension/Utils.cpp @@ -198,6 +198,10 @@ bool RemoveFile(const std::string& filePath) { return false; } +bool IsLambda() { + return getenv("AWS_LAMBDA_FUNCTION_NAME") != nullptr; +} + std::string GetStackTrace() { #if PHP_VERSION_ID >= 80100 diff --git a/lib/php-extension/include/RequestProcessor.h b/lib/php-extension/include/RequestProcessor.h index 2c77f157..e4312963 100644 --- a/lib/php-extension/include/RequestProcessor.h +++ b/lib/php-extension/include/RequestProcessor.h @@ -5,7 +5,7 @@ typedef GoUint8 (*InitInstanceFn)(void* instancePtr, GoString initJson); typedef void (*DestroyInstanceFn)(uint64_t threadId); // Updated typedefs with instance pointer as first parameter -typedef GoUint8 (*RequestProcessorInitFn)(GoString platformName); +typedef GoUint8 (*RequestProcessorInitFn)(GoString platformName, GoUint8 isLambda); typedef GoUint8 (*RequestProcessorContextInitFn)(void* instancePtr, ContextCallback); typedef GoUint8 (*RequestProcessorConfigUpdateFn)(void* instancePtr, GoString initJson); typedef char* (*RequestProcessorOnEventFn)(void* instancePtr, GoInt eventId); diff --git a/lib/php-extension/include/Utils.h b/lib/php-extension/include/Utils.h index ffdf98ff..8672959e 100644 --- a/lib/php-extension/include/Utils.h +++ b/lib/php-extension/include/Utils.h @@ -35,6 +35,8 @@ bool FileExists(const std::string& filePath); bool RemoveFile(const std::string& filePath); +bool IsLambda(); + std::string GetStackTrace(); zend_class_entry* GetFirewallDefaultExceptionCe(); \ No newline at end of file diff --git a/lib/request-processor/globals/globals.go b/lib/request-processor/globals/globals.go index 418cd1d1..01705e57 100644 --- a/lib/request-processor/globals/globals.go +++ b/lib/request-processor/globals/globals.go @@ -80,16 +80,14 @@ func CreateServer(token string) *ServerData { const Version = "1.5.4" -var SocketPath string +var SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" -func isRunDirWritable() bool { - return os.WriteFile("/run/aikido-probe", nil, 0644) == nil && os.Remove("/run/aikido-probe") == nil -} - -func init() { - runDir := "/run/aikido-" + Version - if !isRunDirWritable() { - runDir = "/tmp/aikido-" + Version +// SetRuntimeDir switches the socket directory to /tmp when running on Lambda. +// The flag is passed from C++ (via RequestProcessorInit) because Go's +// os.LookupEnv is unreliable when this shared library is loaded into a +// forked FPM worker. +func SetRuntimeDir(isLambda bool) { + if isLambda { + SocketPath = "/tmp/aikido-" + Version } - SocketPath = runDir + "/aikido-agent.sock" } diff --git a/lib/request-processor/main.go b/lib/request-processor/main.go index 81a3ed06..618e76a5 100644 --- a/lib/request-processor/main.go +++ b/lib/request-processor/main.go @@ -80,7 +80,7 @@ func DestroyInstance(threadID uint64) { } //export RequestProcessorInit -func RequestProcessorInit(platformName string) (initOk bool) { +func RequestProcessorInit(platformName string, isLambda bool) (initOk bool) { defer func() { if r := recover(); r != nil { log.Warn(nil, "Recovered from panic:", r) @@ -88,6 +88,8 @@ func RequestProcessorInit(platformName string) (initOk bool) { } }() + globals.SetRuntimeDir(isLambda) + config.Init(platformName) if globals.EnvironmentConfig.PlatformName != "cli" { From 2fc87201955382657840a5f5c5939948dca0687f Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Fri, 17 Apr 2026 14:33:05 +0300 Subject: [PATCH 11/15] 5s max loop for socket check --- lib/php-extension/Agent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/php-extension/Agent.cpp b/lib/php-extension/Agent.cpp index 9b8d7ef1..2b4bcf0a 100644 --- a/lib/php-extension/Agent.cpp +++ b/lib/php-extension/Agent.cpp @@ -153,11 +153,11 @@ bool Agent::Init() { // On Lambda cold starts, MINIT and the first invoke happen back-to-back, // so the first gRPC call can race against agent startup. Block here - // (up to ~1s) until the agent has bound its Unix socket. On regular + // (up to ~5s) until the agent has bound its Unix socket. On regular // long-running SAPIs (php-fpm, apache, frankenphp) MINIT runs well // before any request, so this wait is unnecessary. if (IsLambda()) { - for (int i = 0; i < 200; i++) { + for (int i = 0; i < 1000; i++) { if (FileExists(aikidoAgentSocketPath)) { AIKIDO_LOG_INFO("Aikido Agent socket ready after %d ms\n", i * 5); return true; From 4b2df346cfd1bf6e988a48daebe42f9d0b08c478 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Fri, 17 Apr 2026 15:09:41 +0300 Subject: [PATCH 12/15] + --- lib/request-processor/globals/globals.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request-processor/globals/globals.go b/lib/request-processor/globals/globals.go index 01705e57..8f4b0c80 100644 --- a/lib/request-processor/globals/globals.go +++ b/lib/request-processor/globals/globals.go @@ -88,6 +88,6 @@ var SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" // forked FPM worker. func SetRuntimeDir(isLambda bool) { if isLambda { - SocketPath = "/tmp/aikido-" + Version + SocketPath = "/tmp/aikido-" + Version + "/aikido-agent.sock" } } From 0c3250eec2929fb8cd7dd0171df4c42f5beab243 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Fri, 17 Apr 2026 15:21:42 +0300 Subject: [PATCH 13/15] + --- lib/agent/constants/constants.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/agent/constants/constants.go b/lib/agent/constants/constants.go index caa3d07d..2f9fe834 100644 --- a/lib/agent/constants/constants.go +++ b/lib/agent/constants/constants.go @@ -1,7 +1,5 @@ package constants -const Version = "1.5.4" - var ( IsLambda bool SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" From 35a1bf2916d84411c9bc2aac69983414f67c68ef Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Fri, 17 Apr 2026 15:24:25 +0300 Subject: [PATCH 14/15] + --- lib/request-processor/globals/globals.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/request-processor/globals/globals.go b/lib/request-processor/globals/globals.go index be768dcd..1167996d 100644 --- a/lib/request-processor/globals/globals.go +++ b/lib/request-processor/globals/globals.go @@ -79,8 +79,7 @@ func CreateServer(token string) *ServerData { } const ( - Version = "1.5.6" - SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" + Version = "1.5.6" ) var SocketPath = "/run/aikido-" + Version + "/aikido-agent.sock" From 48726f16764c3c4af5342769d18319d2c9b81265 Mon Sep 17 00:00:00 2001 From: ioaniftimesei Date: Fri, 17 Apr 2026 15:54:57 +0300 Subject: [PATCH 15/15] Apply suggestions from code review Co-authored-by: aikido-pr-checks[bot] <169896070+aikido-pr-checks[bot]@users.noreply.github.com> --- lib/agent/constants/constants.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/agent/constants/constants.go b/lib/agent/constants/constants.go index 2f9fe834..0853ddb7 100644 --- a/lib/agent/constants/constants.go +++ b/lib/agent/constants/constants.go @@ -11,8 +11,8 @@ var ( // is spawned. func SetRuntimeDir(isLambda bool) { if isLambda { - SocketPath = "/tmp/aikido-" + "/aikido-agent.sock" - PidPath = "/tmp/aikido-" + "/aikido-agent.pid" + SocketPath = "/tmp/aikido-" + Version + "/aikido-agent.sock" + PidPath = "/tmp/aikido-" + Version + "/aikido-agent.pid" } }