SecurityGroup¶
SecurityGroupは、Podに適用するステートフルな許可ルール (allow-list) の集合です。 juneau.loutres.me/security-groups annotationで対象Podに付与すると、そのPodの送受信トラフィックがルールで制限されます。
各SecurityGroupは1つのVpcに属し、同じVpc内のPodおよびSecurityGroup同士でのみ参照できます。
ルールの基本形¶
spec.ingressは、このSecurityGroupに属するPodが受信して良いトラフィックを記述します。 spec.egressは、このSecurityGroupに属するPodが送信して良いトラフィックを記述します。
各ルールは、対向となるピア (from / to)、プロトコル、宛先ポートで構成されます。
apiVersion: juneau.loutres.me/v1alpha1
kind: SecurityGroup
metadata:
name: web-sg
spec:
vpc: app-vpc
ingress:
- from:
- cidr: 10.0.0.0/24
- securityGroupRef:
name: client-sg
protocol: tcp
ports:
- port: 80
- portRange:
from: 8000
to: 8999
egress:
- to:
- cidr: 0.0.0.0/0
protocol: all
ピアの指定方法は2種類で、どちらか一方をルールごとに選びます。
cidr: IPv4 CIDRで対向アドレス範囲を指定securityGroupRef.name: 対向側に付与されたSecurityGroupの名前を指定。同じVpcに属するSecurityGroupだけが参照できます
protocolには、キーワードか0から255のIPプロトコル番号を書きます。キーワードはall / icmp / tcp / udp / sctp / gre / esp / ahの8つです。
キーワードは番号に名前を付けただけのもので、tcpと6は同じルールになります。番号は引用符を付けずに整数で書いてください。"47"のように引用符を付けると文字列として読まれ、その綴りのキーワードは無いので拒否されます。allは全てのIPプロトコルにマッチします。
portsを書けるのはtcpとudpのルールだけです。他のプロトコルにポートを書くと、作成や更新の時点でwebhookが拒否します。SCTPにもポート番号はありますが、data planeがSCTPヘッダを読まないので、書けても効かないルールになります。それでsctpはポートを書けない側に入れてあります。
ports[].portで単一ポート、ports[].portRange.from / toで範囲を指定できます。 portsを省略した場合は、そのプロトコルの全ポートにマッチします。
既定動作¶
spec.ingressを省略 / 空にすると、受信は全て遮断されます (deny-by-default)。1つでも受信を許可したいピアがある場合は、それを明示するルールを追加してくださいspec.egressを省略すると、送信は全て許可されます (default-allow)。送信を制限したい場合は明示的にspec.egressを書いてください- どのSecurityGroupにも属さないPodは、SecurityGroupによる制限を受けません
ステートフルなので、許可された送信に対する応答は受信ルールに無くても通過します。受信側についても同様です。
以前は、TCPとUDPとICMP以外のIPプロトコルがSecurityGroupの評価を素通りしていました。いまは全てのIPプロトコルが評価されます。TCPのルールしか書いていないSecurityGroupを付けたPodには、GREやESPは届きません。トンネルやIPsecを張っているPodがあれば、そのプロトコルを許可するルールを足してください。SecurityGroupを付けていないPodは以前と変わりません。
SecurityGroupを付けたPodは、L4ヘッダを読めないパケットも落とします。ポートが分からないと、ルールと突き合わせようがないからです。fragmentに分かれたTCPとUDPは、先頭のfragmentが持っていたポートを使って後続のfragmentも判定するので、普段はこれに当たりません。当たるのは、fragmentが順番どおりに着かなかった場合です。
IPv4以外のフレームも落とします。juneauが扱うのはIPv4だけなので、SecurityGroupを付けたPodの間でIPv6は使えません。ARPだけは例外で、落とすとPodのネットワークが成立しないため常に通します。
これらはどれも、SecurityGroupを付けていないPodには関係ありません。SubnetにNetworkACLも紐付いていなければ、これまでどおりの動きです。
ルールの上限¶
上限はルールの本数ではなく、data planeが持つエントリ数で数えます。SecurityGroupのルールはピアとポートの直積に展開されるので、1ルールのコストは次のとおりです。
エントリ数 = max(1, from / to の要素数) * max(1, ports の要素数)
portsを省略したルールは全ポートにマッチする1エントリ分として数えます。ピアを3つ、ポートを2つ書いたルールは6エントリを使います。
この予算は方向ごとに独立しています。ingressとegressはBPFのルール配列の別々の区画を使うので、片方を使い切ってももう片方は影響を受けません。両方向の合計で数えることはありません。
SecurityGroupは1方向あたり8エントリまでです。NetworkACLの16より小さいのは、1つのPodに複数のSecurityGroupを付けられるからです。1パケットあたりの走査量は付いているSecurityGroupの数だけ増えるので、1つあたりの本数を抑えてあります。Subnetに紐付くNetworkACLは最大1つなので、そちらは倍を許せます。
from / to / portsに書ける要素数もそれぞれ8までです。ただしこれは1つのリストの上限なので、ピアを8つとポートを8つ書いたルールは64エントリになり、1本だけで上限を超えます。
上限を超えるSecurityGroupは、作成・更新の時点でwebhookが拒否します。つまり存在しているSecurityGroupは、data planeがルールを全て保持できるものだけです。
securityGroupRefで指定したピアが解決できなかった場合、そのピアは展開時に落とされます。実際に使われるエントリ数はここで数えた値以下になり、超えることはありません。
Podへの付与¶
PodのannotationでSecurityGroupを指定します。複数のSecurityGroupはカンマ区切りで列挙できます。
apiVersion: v1
kind: Pod
metadata:
name: web
annotations:
juneau.loutres.me/security-groups: web-sg,monitoring-sg
spec:
containers:
- name: nginx
image: nginx:1.27
複数のSecurityGroupを付けた場合、それぞれのルールはOR (許可のいずれか1つに合致すれば通過) で評価されます。
1つのPodに付与できるSecurityGroupの個数には上限があります。juneau.loutres.me/security-groups annotationで上限を超える数を指定すると、そのPodは作成できません。
参照するSecurityGroupは、Podが属するSubnetのVpcと同じVpcのSecurityGroupでなければなりません。
Vpcによる強制¶
Vpcにspec.enforceSecurityGroups: trueを設定すると、そのVpc配下のSubnetに作られるPodは少なくとも1つのSecurityGroupを付与しなければ作成できません。 セキュリティ要件が厳しい環境で「全てのPodに必ずSecurityGroupを付ける」ポリシーを強制したい場合に使います。
status¶
status.groupIDは、このSecurityGroupに割り当てられたクラスタ内で一意の番号です。Pod側 (NetworkInterfaceのstatus) に展開され、ルールの伝達に使われます。
status.ingressRuleCount / status.egressRuleCountは、spec.ingress / spec.egressに書いたルールの本数です。
status.ingressEntryCount / status.egressEntryCountは、そのルールをピアとポートの直積に展開したあとのエントリ数です。上限と突き合わせるのはこちらの値です。
status.hasEgressRulesは、spec.egressが明示的に指定されているかを示します。指定されていない場合は送信が全許可 (default-allow) として扱われます。
status.attachedInterfacesは、現時点でこのSecurityGroupを参照しているNetworkInterfaceの一覧です。
status.rulesetVersionは、内部的にルール内容が変わるたびに増加する値です。
Conditions¶
Ready: SecurityGroupが正常に解決され、ルールが適用可能な状態になっていればTrueRulesValid:specのルールがすべて正しく解決できていればTrue。参照したSecurityGroupが存在しない、どちらかの方向がエントリ数の上限を超えているなどの場合にFalseになります
制限事項¶
- ルールはallow-listです。明示的なdeny指定は現在サポートしていません
- ルールの優先度はありません。複数SecurityGroupおよび複数ルールの評価結果はORで合成されます
- 1方向に書けるエントリ数は8までです。超えるSecurityGroupはwebhookが拒否するので、作成や更新そのものが失敗します。数え方は「ルールの上限」を参照してください
- IPv6には対応していません
- 削除を試みたSecurityGroupがNetworkInterfaceから参照されている場合、削除は拒否されます。先に該当Podを削除してください