Skip to content

Instantly share code, notes, and snippets.

@pierre-emmanuelJ
Last active May 4, 2020 15:34
Show Gist options
  • Save pierre-emmanuelJ/0e6ecfce3ddbb3b989a14526fdda56e4 to your computer and use it in GitHub Desktop.
Save pierre-emmanuelJ/0e6ecfce3ddbb3b989a14526fdda56e4 to your computer and use it in GitHub Desktop.
A tool to create all security groups for a k8s cluster with Calico at Exoscale
package main
import (
"errors"
"fmt"
"os"
"log"
"github.com/exoscale/egoscale"
)
const defaultComputeEndpoint = "https://api.exoscale.com/v1"
func createK8SSG(sgPrefix string) error {
masterSecurityGroup := sgPrefix + "-master"
nodeSecurityGroup := sgPrefix + "-node"
//XXX can be possible to authorize sg in each other
masterRules := buildMasterFirewallRules(masterSecurityGroup, nodeSecurityGroup)
nodeRules := buildNodeFirewallRules(nodeSecurityGroup, masterSecurityGroup)
masterSG, err := getORCreateSecurityGroup(masterSecurityGroup)
if err != nil {
return err
}
nodeSG, err := getORCreateSecurityGroup(nodeSecurityGroup)
if err != nil {
return err
}
if err := checkSecurityGroup(masterSG.ID, masterRules); err != nil {
return err
}
if err := checkSecurityGroup(nodeSG.ID, nodeRules); err != nil {
return err
}
return nil
}
func getORCreateSecurityGroup(sgName string) (*egoscale.SecurityGroup, error) {
exoClient, err := newExoscaleClient()
if err != nil {
return nil, err
}
resp, err := exoClient.Get(egoscale.SecurityGroup{Name: sgName})
if err != nil {
if e, ok := err.(*egoscale.ErrorResponse); ok {
if e.ErrorCode != egoscale.ParamError {
return nil, err
}
}
}
if err == nil {
return resp.(*egoscale.SecurityGroup), nil
}
resp, err = exoClient.Request(egoscale.CreateSecurityGroup{Name: sgName})
if err != nil {
return nil, fmt.Errorf("error creating network security group: %v", err)
}
return resp.(*egoscale.SecurityGroup), nil
}
func checkSecurityGroup(sgID *egoscale.UUID, rules []egoscale.AuthorizeSecurityGroupIngress) error {
exoClient, err := newExoscaleClient()
if err != nil {
return err
}
resp, err := exoClient.Get(egoscale.SecurityGroup{ID: sgID})
if err != nil {
return err
}
sg := resp.(*egoscale.SecurityGroup)
if len(sg.IngressRule) > 0 {
return nil
}
for _, rule := range rules {
rule.SecurityGroupID = sg.ID
_, err = exoClient.Request(rule)
if err != nil {
return fmt.Errorf("error creating or updating security group rule: %v", err)
}
}
return nil
}
func buildMasterFirewallRules(self, nodeSG string) []egoscale.AuthorizeSecurityGroupIngress {
return []egoscale.AuthorizeSecurityGroupIngress{
{
Protocol: "TCP",
StartPort: 22,
EndPort: 22,
CIDRList: []egoscale.CIDR{
*egoscale.MustParseCIDR("0.0.0.0/0"),
*egoscale.MustParseCIDR("::/0"),
},
Description: "SSH",
},
{
Protocol: "TCP",
StartPort: 6443,
EndPort: 6443,
CIDRList: []egoscale.CIDR{
*egoscale.MustParseCIDR("0.0.0.0/0"),
*egoscale.MustParseCIDR("::/0"),
},
Description: "Kubernetes API server",
},
///XXX TODO if etcd is in an other security group careful you must update this rule
// or make it dynamic if you leave the choice to the provider spec
{
Protocol: "TCP",
StartPort: 2379,
EndPort: 2380,
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
},
Description: "etcd server client API",
},
//XXX if you move Control plane from master sg in an other security group
// please create a new one to accept ingress from Control plane
{
Protocol: "TCP",
StartPort: 10250,
EndPort: 10252,
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
},
Description: "Kubelet API, kube-scheduler, kube-controller-manager",
},
{
Protocol: "TCP",
StartPort: 179,
EndPort: 179,
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
{Group: nodeSG},
},
Description: "Calico BGP",
},
{
Protocol: "TCP",
StartPort: 6666,
EndPort: 6666,
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
{Group: nodeSG},
},
Description: "Calico etcd",
},
{
Protocol: "ipip",
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
{Group: nodeSG},
},
Description: "Calico IPIP",
},
}
}
func buildNodeFirewallRules(self, masterSG string) []egoscale.AuthorizeSecurityGroupIngress {
return []egoscale.AuthorizeSecurityGroupIngress{
{
Protocol: "TCP",
StartPort: 22,
EndPort: 22,
CIDRList: []egoscale.CIDR{
*egoscale.MustParseCIDR("0.0.0.0/0"),
*egoscale.MustParseCIDR("::/0"),
},
Description: "SSH",
},
//XXX if you move Control plane from master sg in an other security group please update this rule
{
Protocol: "TCP",
StartPort: 10250,
EndPort: 10250,
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
{Group: masterSG},
},
Description: "Kubelet API",
},
{
Protocol: "TCP",
StartPort: 30000,
EndPort: 32767,
Description: "NodePort Services",
CIDRList: []egoscale.CIDR{
*egoscale.MustParseCIDR("0.0.0.0/0"),
*egoscale.MustParseCIDR("::/0"),
},
},
{
Protocol: "TCP",
StartPort: 179,
EndPort: 179,
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
{Group: masterSG},
},
Description: "Calico BGP",
},
{
Protocol: "ipip",
UserSecurityGroupList: []egoscale.UserSecurityGroup{
{Group: self},
{Group: masterSG},
},
Description: "Calico IPIP",
},
}
}
func newExoscaleClient() (*egoscale.Client, error) {
envEndpoint := os.Getenv("EXOSCALE_API_ENDPOINT")
envKey := os.Getenv("EXOSCALE_API_KEY")
envSecret := os.Getenv("EXOSCALE_API_SECRET")
if envEndpoint == "" {
envEndpoint = defaultComputeEndpoint
}
if envKey == "" || envSecret == "" {
return nil, errors.New("incomplete or missing for API credentials")
}
return egoscale.NewClient(envEndpoint, envKey, envSecret), nil
}
func main() {
if len(os.Args) != 2 {
fmt.Printf("%s <sg cluster prefix>\n", os.Args[0])
os.Exit(1)
}
err := createK8SSG(os.Args[1])
if err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment