Skip to content

Conversation

tudor-timcu
Copy link
Collaborator

@tudor-timcu tudor-timcu commented Oct 13, 2025

  • Added support for only one Aikido Agent that will be spawned for each machine
    • The request processor instances check on startup if an agent is already running and connect to that socket
    • Checking is done using the socket file on the disk and by checking the running PID
    • Fallbacks are in place if the PID is not running, but sockets still on disk
  • The new Agent support any number of different servers being hosted on the same machine (virtual hosts, multiple php servers with different versions)
    • This works by having a global map in the Aikido Agent that stores each different server instance with it's correspoding data
    • On the first request of a new server, the request processor registers into the Agent by providing the token
    • The agent creates a new entry in the global map, spawns necessary goroutines for that server, initializes it's custom global data and it's live from now on
    • The agent won't even register that server if a token it's not provided, it will do so only after the first successful token registration by the request processor
  • This also fixes various crashes and inconsistencies on the restart mechanisms of apache / php-fpm, as the agent is not unloaded anymore and just re-used across restarts.
  • The agent is stopped only on uninstall
  • The E2E tests were updated to use this exact same mechanism, each test has it's own mock token that registers into one Agent that all the 60+ tests use on a single machine
  • This is more in line with industry standards of having only one Agent per machine and allows us to further optimize things in the future by having all the PHP-related servers report through only one agent

/* Construct the AttackDetected protobuf structure to be sent via gRPC to the Agent */
func GetAttackDetectedProto(res utils.InterceptorResult) *protos.AttackDetected {
return &protos.AttackDetected{
Token: globals.AikidoConfig.Token,
Copy link
Contributor

@aikido-pr-checks aikido-pr-checks bot Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetAttackDetectedProto now reads globals.AikidoConfig.Token (hidden global dependency), mixing configuration access into message construction.

Feedback

Post a comment with the following structure to provide feedback on this finding:

@AikidoSec feedback: [FEEDBACK]

Aikido will process this feedback into learnings to give better review comments in the future.
More info

@tudor-timcu tudor-timcu changed the title Aikido Agent standalone One Agent to rule them all Oct 20, 2025

func serversCleanupRoutine(server *ServerData) {
for _, token := range globals.GetServersTokens() {
server := globals.GetServer(token)
Copy link
Contributor

@aikido-pr-checks aikido-pr-checks bot Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function parameter 'server' is shadowed by local declaration 'server := globals.GetServer(token)'.

Feedback

Post a comment with the following structure to provide feedback on this finding:

@AikidoSec feedback: [FEEDBACK]

Aikido will process this feedback into learnings to give better review comments in the future.
More info

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems a good one to fix? ^^

return agentPID;
}

bool Agent::RemoveSocketFiles() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function RemoveSocketFiles returns a boolean named 'failed' (true on failure), making the return semantics ambiguous/inverted and prone to incorrect usage.

Feedback

Post a comment with the following structure to provide feedback on this finding:

@AikidoSec feedback: [FEEDBACK]

Aikido will process this feedback into learnings to give better review comments in the future.
More info

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems valid, see:

if (!this->RemoveSocketFiles()) {
      AIKIDO_LOG_WARN("Failed to remove some socket files, will try to re-spawn Aikido Agent...\n");
  } else {
      AIKIDO_LOG_INFO("Successfully removed old socket files!\n");
  }

if now-lastConnectionTime > MinServerInactivityForCleanup {
// Server has been inactive for more than 10 minutes
log.Infof("Server has been inactive for more than 10 minutes, unregistering...")
server_utils.Unregister(token)
Copy link
Member

@hansott hansott Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a brief chance that we might delete a just created server because LastConnectionTime is 0 until GetCloudConfig is called? Should we set LastConnectionTime on creation of a server?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very good point, thanks for this

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

while ((ent = readdir(dir)) != NULL) {
std::string filename(ent->d_name);
if (filename.find(".sock") != std::string::npos) {
socketPath = aikidoRunFolder + "/" + filename;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prob good idea to test connection? ping / pong on gRPC?

Copy link
Member

@hansott hansott Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to grab latest one? readdir sorting might be random?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in theory there should be only one there, but indeed we might end up with 2 sockets. Not sure how I would test the grpc connection over the socket, but will look to make this more robust.

if (this->agentPid != 0) {
AIKIDO_LOG_WARN("Aikido Agent already running!\n");
return true;
std::string Agent::GetSocketPath() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class Agent now mixes filesystem discovery, /proc parsing, and process spawning responsibilities (single responsibility violation).

Feedback

Post a comment with the following structure to provide feedback on this finding:

@AikidoSec feedback: [FEEDBACK]

Aikido will process this feedback into learnings to give better review comments in the future.
More info

if (this->agentPid == 0) {
AIKIDO_LOG_WARN("Aikido Agent not running!\n");
return;
bool Agent::SpawnDetached(std::string aikidoAgentPath, std::string initData, std::string token) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function SpawnDetached performs forking and daemonization without explanatory comments.

Feedback

Post a comment with the following structure to provide feedback on this finding:

@AikidoSec feedback: [FEEDBACK]

Aikido will process this feedback into learnings to give better review comments in the future.
More info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants