Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report NGINX App Protect instances #942

Open
wants to merge 2 commits into
base: chore/add-nap-to-mock-collector
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
503 changes: 305 additions & 198 deletions api/grpc/mpi/v1/command.pb.go

Large diffs are not rendered by default.

149 changes: 149 additions & 0 deletions api/grpc/mpi/v1/command.pb.validate.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions api/grpc/mpi/v1/command.proto
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ message InstanceMeta {
INSTANCE_TYPE_NGINX_PLUS = 3;
// NGINX Unit
INSTANCE_TYPE_UNIT = 4;
// NGINX App Protect
INSTANCE_TYPE_NGINX_APP_PROTECT = 5;
}
// the types of instances possible
InstanceType instance_type = 2;
Expand Down Expand Up @@ -282,6 +284,8 @@ message InstanceRuntime {
NGINXRuntimeInfo nginx_runtime_info = 4;
// NGINX Plus runtime configuration settings like api value, usually read from the NGINX config, NGINX process or NGINX Plus API
NGINXPlusRuntimeInfo nginx_plus_runtime_info = 5;
// NGINX App Protect runtime information
NGINXAppProtectRuntimeInfo nginx_app_protect_runtime_info = 7;
}
// List of worker processes
repeated InstanceChild instance_children = 6;
Expand Down Expand Up @@ -329,6 +333,16 @@ message APIDetails {
string listen = 2;
}

// A set of runtime NGINX App Protect settings
message NGINXAppProtectRuntimeInfo {
// NGINX App Protect Release
string release = 1;
// Attack signature version
string attack_signature_version = 2;
// Threat campaign version
string threat_campaign_version = 3;
}

// A set of actions that can be performed on an instance
message InstanceAction {}

Expand Down
20 changes: 20 additions & 0 deletions docs/proto/protos.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
- [InstanceRuntime](#mpi-v1-InstanceRuntime)
- [ManagementPlaneRequest](#mpi-v1-ManagementPlaneRequest)
- [MetricsServer](#mpi-v1-MetricsServer)
- [NGINXAppProtectRuntimeInfo](#mpi-v1-NGINXAppProtectRuntimeInfo)
- [NGINXPlusAction](#mpi-v1-NGINXPlusAction)
- [NGINXPlusRuntimeInfo](#mpi-v1-NGINXPlusRuntimeInfo)
- [NGINXRuntimeInfo](#mpi-v1-NGINXRuntimeInfo)
Expand Down Expand Up @@ -929,6 +930,7 @@ Meta-information relating to the reported instance
| config_path | [string](#string) | | the config path location |
| nginx_runtime_info | [NGINXRuntimeInfo](#mpi-v1-NGINXRuntimeInfo) | | NGINX runtime configuration settings like stub_status, usually read from the NGINX config or NGINX process |
| nginx_plus_runtime_info | [NGINXPlusRuntimeInfo](#mpi-v1-NGINXPlusRuntimeInfo) | | NGINX Plus runtime configuration settings like api value, usually read from the NGINX config, NGINX process or NGINX Plus API |
| nginx_app_protect_runtime_info | [NGINXAppProtectRuntimeInfo](#mpi-v1-NGINXAppProtectRuntimeInfo) | | NGINX App Protect runtime information |
| instance_children | [InstanceChild](#mpi-v1-InstanceChild) | repeated | List of worker processes |


Expand Down Expand Up @@ -967,6 +969,23 @@ The metrics settings associated with origins (sources) of the metrics and destin



<a name="mpi-v1-NGINXAppProtectRuntimeInfo"></a>

### NGINXAppProtectRuntimeInfo
A set of runtime NGINX App Protect settings


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| release | [string](#string) | | NGINX App Protect Release |
| attack_signature_version | [string](#string) | | Attack signature version |
| threat_campaign_version | [string](#string) | | Threat campaign version |






<a name="mpi-v1-NGINXPlusAction"></a>

### NGINXPlusAction
Expand Down Expand Up @@ -1165,6 +1184,7 @@ the types of instances possible
| INSTANCE_TYPE_NGINX | 2 | NGINX |
| INSTANCE_TYPE_NGINX_PLUS | 3 | NGINX Plus |
| INSTANCE_TYPE_UNIT | 4 | NGINX Unit |
| INSTANCE_TYPE_NGINX_APP_PROTECT | 5 | NGINX App Protect |



Expand Down
3 changes: 2 additions & 1 deletion internal/watcher/health/health_watcher_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ func (hw *HealthWatcherService) AddHealthWatcher(instances []*mpi.Instance) {
hw.watchers[instance.GetInstanceMeta().GetInstanceId()] = watcher
case mpi.InstanceMeta_INSTANCE_TYPE_AGENT:
case mpi.InstanceMeta_INSTANCE_TYPE_UNSPECIFIED,
mpi.InstanceMeta_INSTANCE_TYPE_UNIT:
mpi.InstanceMeta_INSTANCE_TYPE_UNIT,
mpi.InstanceMeta_INSTANCE_TYPE_NGINX_APP_PROTECT:
fallthrough
default:
slog.Warn("Health watcher not implemented", "instance_type",
Expand Down
1 change: 1 addition & 0 deletions internal/watcher/instance/instance_watcher_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func NewInstanceWatcherService(agentConfig *config.Config) *InstanceWatcherServi
processOperator: process.NewProcessOperator(),
processParsers: []processParser{
NewNginxProcessParser(),
NewNginxAppProtectProcessParser(),
},
nginxConfigParser: NewNginxConfigParser(agentConfig),
instanceCache: make(map[string]*mpi.Instance),
Expand Down
135 changes: 135 additions & 0 deletions internal/watcher/instance/nginx_app_protect_process_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright (c) F5, Inc.
//
// This source code is licensed under the Apache License, Version 2.0 license found in the
// LICENSE file in the root directory of this source tree.

package instance

import (
"context"
"log/slog"
"os"
"strings"

mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1"
"github.com/nginx/agent/v3/internal/model"
"github.com/nginx/agent/v3/pkg/uuid"
)

const (
versionFilePath = "/opt/app_protect/VERSION"
releaseFilePath = "/opt/app_protect/RELEASE"
processName = "bd-socket-plugin"
attackSignatureVersionFilePath = "/opt/app_protect/var/update_files/signatures/version"
threatCampaignVersionFilePath = "/opt/app_protect/var/update_files/threat_campaigns/version"
)

type (
NginxAppProtectProcessParser struct {
versionFilePath string
releaseFilePath string
attackSignatureVersionFilePath string
threatCampaignVersionFilePath string
}
)

var _ processParser = (*NginxAppProtectProcessParser)(nil)

func NewNginxAppProtectProcessParser() *NginxAppProtectProcessParser {
return &NginxAppProtectProcessParser{
versionFilePath: versionFilePath,
releaseFilePath: releaseFilePath,
attackSignatureVersionFilePath: attackSignatureVersionFilePath,
threatCampaignVersionFilePath: threatCampaignVersionFilePath,
}
}

func (n NginxAppProtectProcessParser) Parse(ctx context.Context, processes []*model.Process) map[string]*mpi.Instance {
instanceMap := make(map[string]*mpi.Instance) // key is instanceID

for _, process := range processes {
if process.Name == processName {
instanceID := n.instanceID(process)

instanceMap[instanceID] = &mpi.Instance{
InstanceMeta: &mpi.InstanceMeta{
InstanceId: instanceID,
InstanceType: mpi.InstanceMeta_INSTANCE_TYPE_NGINX_APP_PROTECT,
Version: n.instanceVersion(ctx),
},
InstanceConfig: &mpi.InstanceConfig{},
InstanceRuntime: &mpi.InstanceRuntime{
ProcessId: process.PID,
BinaryPath: process.Exe,
ConfigPath: "",
Copy link
Contributor

Choose a reason for hiding this comment

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

should we populate this with the config location for NAP?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

wasn't sure how to get the config path for NAP so I left it blank for now

Details: &mpi.InstanceRuntime_NginxAppProtectRuntimeInfo{
NginxAppProtectRuntimeInfo: &mpi.NGINXAppProtectRuntimeInfo{
Release: n.release(ctx),
AttackSignatureVersion: n.attackSignatureVersion(ctx),
ThreatCampaignVersion: n.threatCampaignVersion(ctx),
},
},
InstanceChildren: make([]*mpi.InstanceChild, 0),
},
}
}
}

return instanceMap
}

func (n NginxAppProtectProcessParser) instanceID(process *model.Process) string {
return uuid.Generate("%s", process.Exe)
}

func (n NginxAppProtectProcessParser) instanceVersion(ctx context.Context) string {
version, err := os.ReadFile(n.versionFilePath)
if err != nil {
slog.WarnContext(ctx, "Unable to read NAP version file", "file_path", n.versionFilePath, "error", err)
return ""
}

return strings.TrimSuffix(string(version), "\n")
}

func (n NginxAppProtectProcessParser) release(ctx context.Context) string {
release, err := os.ReadFile(n.releaseFilePath)
if err != nil {
slog.WarnContext(ctx, "Unable to read NAP release file", "file_path", n.releaseFilePath, "error", err)
return ""
}

return strings.TrimSuffix(string(release), "\n")
}

func (n NginxAppProtectProcessParser) attackSignatureVersion(ctx context.Context) string {
attackSignatureVersion, err := os.ReadFile(n.attackSignatureVersionFilePath)
if err != nil {
slog.WarnContext(
ctx,
"Unable to read NAP attack signature version file",
"file_path", n.attackSignatureVersionFilePath,
"error", err,
)

return ""
}

return string(attackSignatureVersion)
}

func (n NginxAppProtectProcessParser) threatCampaignVersion(ctx context.Context) string {
threatCampaignVersion, err := os.ReadFile(n.threatCampaignVersionFilePath)
if err != nil {
slog.WarnContext(
ctx,
"Unable to read NAP threat campaign version file",
"file_path", n.threatCampaignVersionFilePath,
"error", err,
)

return ""
}

return string(threatCampaignVersion)
}
Loading