Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions jobs/galera-agent/spec
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ templates:

packages:
- galera-agent
- bosh-monit-access

provides:
- name: galera-agent
Expand Down
32 changes: 20 additions & 12 deletions jobs/galera-agent/templates/service.bash
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@

set -o nounset

if type -f -p nft >/dev/null && nft list ruleset | grep -q monit_output; then
rule_handle=$(nft -a list ruleset | awk '/galera-agent/ { print $NF }')
if [[ -n $rule_handle ]]; then
nft delete rule inet filter monit_output handle "${rule_handle}"
# Setup firewall rule to allow monit access from this job.
# Try new nftables firewall approach first (bosh-agent with monit_access_jobs chain).
if /var/vcap/packages/bosh-monit-access/bin/bosh-monit-access --check; then
# New firewall with jobs chain exists - use bosh-monit-access helper
/var/vcap/packages/bosh-monit-access/bin/bosh-monit-access 1>&2
else
# Fallback to old approaches for backward compatibility with older stemcells
if type -f -p nft >/dev/null && nft list ruleset | grep -q monit_output; then
rule_handle=$(nft -a list ruleset | awk '/galera-agent/ { print $NF }')
if [[ -n $rule_handle ]]; then
nft delete rule inet filter monit_output handle "${rule_handle}"
fi
nft insert rule inet filter monit_output index 2 \
socket cgroupv2 level 2 "system.slice/runc-bpm-galera-agent.scope" \
ip daddr 127.0.0.1 tcp dport 2822 \
log prefix '"Matched cgroup galera-agent monit access rule: "' \
accept
elif [[ -f /var/vcap/bosh/etc/monit-access-helper.sh ]]; then
source /var/vcap/bosh/etc/monit-access-helper.sh
permit_monit_access
fi
nft insert rule inet filter monit_output index 2 \
socket cgroupv2 level 2 "system.slice/runc-bpm-galera-agent.scope" \
ip daddr 127.0.0.1 tcp dport 2822 \
log prefix '"Matched cgroup galera-agent monit access rule: "' \
accept
elif [[ -f /var/vcap/bosh/etc/monit-access-helper.sh ]]; then
source /var/vcap/bosh/etc/monit-access-helper.sh
permit_monit_access
fi

# unmount fails under newer Ubuntu kernels without using the "--make-rslave" option
Expand Down
8 changes: 8 additions & 0 deletions packages/bosh-monit-access/packaging
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
set -e -x

source /var/vcap/packages/golang-1-linux/bosh/compile.env

pushd bosh-monit-access
go build -o "${BOSH_INSTALL_TARGET}/bin/bosh-monit-access" .
popd
8 changes: 8 additions & 0 deletions packages/bosh-monit-access/spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
name: bosh-monit-access

dependencies:
- golang-1-linux

files:
- bosh-monit-access/**/*
81 changes: 81 additions & 0 deletions src/bosh-monit-access/cgroup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//go:build linux

package main

import (
"fmt"
"os"
"path/filepath"
"strings"
"syscall"
)

// getCurrentCgroupPath reads /proc/self/cgroup and extracts the cgroupv2 path.
// Returns path WITHOUT leading slash (e.g., "system.slice/runc-bpm-galera-agent.scope")
// to match the format used by the nft CLI.
func getCurrentCgroupPath() (string, error) {
data, err := os.ReadFile("/proc/self/cgroup")
if err != nil {
return "", fmt.Errorf("reading /proc/self/cgroup: %w", err)
}

// Find line starting with "0::" (cgroupv2)
// Format: "0::/system.slice/runc-bpm-galera-agent.scope"
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "0::") {
path := strings.TrimPrefix(line, "0::")
// Strip leading slash to match Noble script format
path = strings.TrimPrefix(path, "/")
return path, nil
}
}

return "", fmt.Errorf("cgroupv2 path not found in /proc/self/cgroup")
}

// isCgroupAccessible checks if the cgroup path is accessible and functional
// for nftables socket cgroupv2 matching.
//
// This returns false in these cases:
// - Cgroup path doesn't exist in /sys/fs/cgroup
// - Hybrid cgroup system (cgroupv2 mounted but no controllers delegated)
// - Nested containers where cgroup path is different from host view
func isCgroupAccessible(cgroupPath string) bool {
// Check if cgroup path exists
fullPath := filepath.Join("/sys/fs/cgroup", cgroupPath)
if _, err := os.Stat(fullPath); err != nil {
fmt.Printf("bosh-monit-access: Cgroup path doesn't exist: %s\n", fullPath)
return false
}

// Check if this is a hybrid cgroup system (cgroupv2 mounted but no controllers)
// On hybrid systems, /sys/fs/cgroup/cgroup.controllers exists but is empty
controllers, err := os.ReadFile("/sys/fs/cgroup/cgroup.controllers")
if err != nil {
fmt.Printf("bosh-monit-access: Cannot read cgroup.controllers: %v\n", err)
return false
}

if len(strings.TrimSpace(string(controllers))) == 0 {
fmt.Println("bosh-monit-access: Hybrid cgroup system detected (no controllers in cgroupv2)")
return false
}

return true
}

// getCgroupInodeID returns the inode ID for the cgroup path.
// The nftables kernel expects an 8-byte cgroup inode ID for 'socket cgroupv2'
// matching, NOT the path string. The nft CLI translates paths to inode IDs
// automatically, but the Go library requires manual lookup.
func getCgroupInodeID(cgroupPath string) (uint64, error) {
fullPath := filepath.Join("/sys/fs/cgroup", cgroupPath)

var stat syscall.Stat_t
if err := syscall.Stat(fullPath, &stat); err != nil {
return 0, fmt.Errorf("stat %s: %w", fullPath, err)
}

return stat.Ino, nil
}
Loading