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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,37 @@ sudo cocoon-net init \
--pool-size 140
```

#### VM egress isolation (`--drop-internal-access`, `--drop-cidr`)

Both flags (accepted by `init` and `adopt`) block VM-originated traffic and are
persisted to `pool.json`, reapplied by the daemon as `FORWARD` DROP rules at the
head of the chain so they win over the default accept rules. Return traffic and
internet egress are unaffected.

- `--drop-internal-access` blocks **VM-to-VM** traffic within the cocoon subnet.
cocoon-net already knows the subnet from `--subnet`, so there is no CIDR to
restate.
- `--drop-cidr` (repeatable) blocks additional **external** destination ranges,
e.g. internal/VPC management networks.

Same-node VMs share `cni0` and are switched at L2, which bypasses iptables
unless `bridge-nf-call-iptables=1`. When either flag is set, node setup loads
`br_netfilter` and enables that toggle, **failing closed** if it cannot — so the
isolation is never silently a no-op. The DROP rules are tagged `cocoon-net-drop`,
so `teardown` removes exactly them.

```bash
sudo cocoon-net init \
--platform gke --node-name cocoon-pool \
--subnet 172.20.100.0/24 --pool-size 140 \
--drop-internal-access \
--drop-cidr 10.0.0.0/8
```

> Note: traffic to the node's own address (e.g. a kubelet bound on the cni0
> gateway IP) is delivered via `INPUT`, not `FORWARD`, so these flags do not
> cover it — restrict those separately (host `INPUT` rule or bind off cni0).

### daemon -- run DHCP server (systemd service)

```bash
Expand Down
34 changes: 20 additions & 14 deletions cmd/adopt.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func runAdopt(cmd *cobra.Command, _ []string) error {
fmt.Printf(" pool-size: 0\n")
}
fmt.Printf(" dns: %s\n", strings.Join(dnsServers, ","))
fmt.Printf(" drop-internal: %v\n", flagDropInternal)
fmt.Printf(" drop-cidr: %s\n", strings.Join(flagDropCIDRs, ","))
fmt.Printf(" state-dir: %s\n", flagStateDir)
fmt.Printf(" manage-iptables: %v\n", flagManageIPTables)
fmt.Println()
Expand All @@ -90,27 +92,31 @@ func runAdopt(cmd *cobra.Command, _ []string) error {
}

nodeCfg := &node.Config{
Gateway: gateway,
SubnetCIDR: flagSubnet,
PrimaryNIC: primaryNIC,
SecondaryNICs: secondaryNICs,
SkipIPTables: !flagManageIPTables,
Gateway: gateway,
SubnetCIDR: flagSubnet,
PrimaryNIC: primaryNIC,
SecondaryNICs: secondaryNICs,
SkipIPTables: !flagManageIPTables,
DropInternalAccess: flagDropInternal,
DropCIDRs: flagDropCIDRs,
}
if err := node.Setup(ctx, nodeCfg); err != nil {
return fmt.Errorf("node setup: %w", err)
}
logger.Info(ctx, "node networking configured (adopted, cloud side untouched)")

state := &pool.State{
Platform: platformName,
NodeName: flagNodeName,
Subnet: flagSubnet,
Gateway: gateway,
PrimaryNIC: primaryNIC,
SecondaryNICs: secondaryNICs,
IPs: ips,
DNSServers: dnsServers,
StateDir: flagStateDir,
Platform: platformName,
NodeName: flagNodeName,
Subnet: flagSubnet,
Gateway: gateway,
PrimaryNIC: primaryNIC,
SecondaryNICs: secondaryNICs,
IPs: ips,
DNSServers: dnsServers,
DropInternalAccess: flagDropInternal,
DropCIDRs: flagDropCIDRs,
StateDir: flagStateDir,
}
if err := state.Save(ctx); err != nil {
return fmt.Errorf("save pool state: %w", err)
Expand Down
12 changes: 7 additions & 5 deletions cmd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ func runDaemon(cmd *cobra.Command, _ []string) error {
primaryNIC = platform.DefaultNIC(state.Platform)
}
if setupErr := node.Setup(ctx, &node.Config{
Gateway: state.Gateway,
SubnetCIDR: state.Subnet,
PrimaryNIC: primaryNIC,
SecondaryNICs: state.SecondaryNICs,
SkipIPTables: flagSkipIPTables,
Gateway: state.Gateway,
SubnetCIDR: state.Subnet,
PrimaryNIC: primaryNIC,
SecondaryNICs: state.SecondaryNICs,
SkipIPTables: flagSkipIPTables,
DropInternalAccess: state.DropInternalAccess,
DropCIDRs: state.DropCIDRs,
}); setupErr != nil {
return fmt.Errorf("node setup: %w", setupErr)
}
Expand Down
22 changes: 13 additions & 9 deletions cmd/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ import (
const defaultStateDir = "/var/lib/cocoon/net"

var (
flagPlatform string
flagNodeName string
flagSubnet string
flagPoolSize int
flagGateway string
flagPrimaryNIC string
flagDNS string
flagStateDir string
flagDryRun bool
flagPlatform string
flagNodeName string
flagSubnet string
flagPoolSize int
flagGateway string
flagPrimaryNIC string
flagDNS string
flagStateDir string
flagDryRun bool
flagDropInternal bool
flagDropCIDRs []string

// flagManageIPTables is the inverse of node.Config.SkipIPTables,
// exposed only on adopt (off by default to preserve host rules).
Expand All @@ -41,6 +43,8 @@ func registerCommonFlags(cmd *cobra.Command, defaultPoolSize int) {
cmd.Flags().StringVar(&flagDNS, "dns", "8.8.8.8,1.1.1.1", "comma-separated DNS servers for DHCP clients")
cmd.Flags().StringVar(&flagStateDir, "state-dir", defaultStateDir, "state directory")
cmd.Flags().BoolVar(&flagDryRun, "dry-run", false, "show what would be done without making changes")
cmd.Flags().BoolVar(&flagDropInternal, "drop-internal-access", false, "block VM-to-VM traffic within the cocoon subnet")
cmd.Flags().StringArrayVar(&flagDropCIDRs, "drop-cidr", nil, "additional external destination CIDR VMs may not reach; repeatable (e.g. --drop-cidr 10.0.0.0/8)")
_ = cmd.MarkFlagRequired("node-name")
_ = cmd.MarkFlagRequired("subnet")
}
Expand Down
34 changes: 20 additions & 14 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func runInit(cmd *cobra.Command, _ []string) error {
fmt.Printf(" subnet: %s\n", cfg.SubnetCIDR)
fmt.Printf(" pool-size: %d\n", cfg.PoolSize)
fmt.Printf(" dns: %s\n", strings.Join(cfg.DNSServers, ","))
fmt.Printf(" drop-internal: %v\n", flagDropInternal)
fmt.Printf(" drop-cidr: %s\n", strings.Join(flagDropCIDRs, ","))
fmt.Printf(" state-dir: %s\n", flagStateDir)
return nil
}
Expand All @@ -66,27 +68,31 @@ func runInit(cmd *cobra.Command, _ []string) error {
logger.Infof(ctx, "provisioned %d IPs on subnet %s", len(result.IPs), result.SubnetCIDR)

nodeCfg := &node.Config{
Gateway: result.Gateway,
SubnetCIDR: result.SubnetCIDR,
PrimaryNIC: result.PrimaryNIC,
SecondaryNICs: result.SecondaryNICs,
Gateway: result.Gateway,
SubnetCIDR: result.SubnetCIDR,
PrimaryNIC: result.PrimaryNIC,
SecondaryNICs: result.SecondaryNICs,
DropInternalAccess: flagDropInternal,
DropCIDRs: flagDropCIDRs,
}
if err := node.Setup(ctx, nodeCfg); err != nil {
return fmt.Errorf("node setup: %w", err)
}
logger.Info(ctx, "node networking configured")

state := &pool.State{
Platform: result.Platform,
NodeName: cfg.NodeName,
Subnet: result.SubnetCIDR,
Gateway: result.Gateway,
PrimaryNIC: result.PrimaryNIC,
SecondaryNICs: result.SecondaryNICs,
IPs: result.IPs,
AliasRangeName: result.AliasRangeName,
DNSServers: dnsServers,
StateDir: flagStateDir,
Platform: result.Platform,
NodeName: cfg.NodeName,
Subnet: result.SubnetCIDR,
Gateway: result.Gateway,
PrimaryNIC: result.PrimaryNIC,
SecondaryNICs: result.SecondaryNICs,
IPs: result.IPs,
AliasRangeName: result.AliasRangeName,
DNSServers: dnsServers,
DropInternalAccess: flagDropInternal,
DropCIDRs: flagDropCIDRs,
StateDir: flagStateDir,
}
if err := state.Save(ctx); err != nil {
return fmt.Errorf("save pool state: %w", err)
Expand Down
5 changes: 5 additions & 0 deletions cmd/teardown.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/projecteru2/core/log"
"github.com/spf13/cobra"

"github.com/cocoonstack/cocoon-net/node"
"github.com/cocoonstack/cocoon-net/platform"
)

Expand Down Expand Up @@ -57,6 +58,10 @@ func runTeardown(cmd *cobra.Command, _ []string) error {
}
logger.Infof(ctx, "teardown complete for %s", state.Platform)

if err := node.ClearDropRules(ctx); err != nil {
logger.Warnf(ctx, "clear iptables drop rules: %v", err)
}

if err := state.Delete(ctx); err != nil {
logger.Warnf(ctx, "delete pool state: %v", err)
}
Expand Down
Loading