feat(depends): add RBAC
This commit is contained in:
3
go.mod
3
go.mod
@@ -9,6 +9,7 @@ require (
|
|||||||
github.com/invopop/jsonschema v0.13.0
|
github.com/invopop/jsonschema v0.13.0
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1
|
github.com/mitchellh/go-wordwrap v1.0.1
|
||||||
github.com/spf13/cobra v1.10.1
|
github.com/spf13/cobra v1.10.1
|
||||||
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/thediveo/netdb v1.1.2
|
github.com/thediveo/netdb v1.1.2
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
k8s.io/api v0.34.1
|
k8s.io/api v0.34.1
|
||||||
@@ -19,6 +20,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||||
github.com/buger/jsonparser v1.1.1 // indirect
|
github.com/buger/jsonparser v1.1.1 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/go-connections v0.6.0 // indirect
|
github.com/docker/go-connections v0.6.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
@@ -35,6 +37,7 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/spf13/pflag v1.0.10 // indirect
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
|||||||
@@ -33,15 +33,16 @@ type ConfigMapMount struct {
|
|||||||
|
|
||||||
// Deployment is a kubernetes Deployment.
|
// Deployment is a kubernetes Deployment.
|
||||||
type Deployment struct {
|
type Deployment struct {
|
||||||
*appsv1.Deployment `yaml:",inline"`
|
*appsv1.Deployment `yaml:",inline"`
|
||||||
chart *HelmChart `yaml:"-"`
|
chart *HelmChart `yaml:"-"`
|
||||||
configMaps map[string]*ConfigMapMount `yaml:"-"`
|
configMaps map[string]*ConfigMapMount `yaml:"-"`
|
||||||
volumeMap map[string]string `yaml:"-"` // keep map of fixed named to original volume name
|
volumeMap map[string]string `yaml:"-"` // keep map of fixed named to original volume name
|
||||||
service *types.ServiceConfig `yaml:"-"`
|
service *types.ServiceConfig `yaml:"-"`
|
||||||
defaultTag string `yaml:"-"`
|
defaultTag string `yaml:"-"`
|
||||||
isMainApp bool `yaml:"-"`
|
isMainApp bool `yaml:"-"`
|
||||||
exchangesVolumes map[string]*labelstructs.ExchangeVolume `yaml:"-"`
|
exchangesVolumes map[string]*labelstructs.ExchangeVolume `yaml:"-"`
|
||||||
boundEnvVar []string `yaml:"-"` // environement to remove
|
boundEnvVar []string `yaml:"-"` // environement to remove
|
||||||
|
needsServiceAccount bool `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDeployment creates a new Deployment from a compose service. The appName is the name of the application taken from the project name.
|
// NewDeployment creates a new Deployment from a compose service. The appName is the name of the application taken from the project name.
|
||||||
@@ -273,6 +274,7 @@ func (d *Deployment) DependsOn(to *Deployment, servicename string) error {
|
|||||||
return d.dependsOnLegacy(to, servicename)
|
return d.dependsOnLegacy(to, servicename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.needsServiceAccount = true
|
||||||
return d.dependsOnK8sAPI(to)
|
return d.dependsOnK8sAPI(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,7 +613,7 @@ func (d *Deployment) Yaml() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// manage serviceAccount, add condition to use the serviceAccount from values.yaml
|
// manage serviceAccount, add condition to use the serviceAccount from values.yaml
|
||||||
if strings.Contains(line, "serviceAccountName:") {
|
if strings.Contains(line, "serviceAccountName:") && !d.needsServiceAccount {
|
||||||
spaces = strings.Repeat(" ", utils.CountStartingSpaces(line))
|
spaces = strings.Repeat(" ", utils.CountStartingSpaces(line))
|
||||||
pre := spaces + `{{- if ne .Values.` + serviceName + `.serviceAccount "" }}`
|
pre := spaces + `{{- if ne .Values.` + serviceName + `.serviceAccount "" }}`
|
||||||
post := spaces + "{{- end }}"
|
post := spaces + "{{- end }}"
|
||||||
@@ -647,6 +649,13 @@ func (d *Deployment) Yaml() ([]byte, error) {
|
|||||||
return []byte(strings.Join(content, "\n")), nil
|
return []byte(strings.Join(content, "\n")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Deployment) SetServiceAccountName() {
|
||||||
|
if d.needsServiceAccount {
|
||||||
|
d.Spec.Template.Spec.ServiceAccountName = utils.TplName(d.service.Name, d.chart.Name)
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Deployment) appendDirectoryToConfigMap(service types.ServiceConfig, appName string, volume types.ServiceVolumeConfig) {
|
func (d *Deployment) appendDirectoryToConfigMap(service types.ServiceConfig, appName string, volume types.ServiceVolumeConfig) {
|
||||||
pathnme := utils.PathToName(volume.Source)
|
pathnme := utils.PathToName(volume.Source)
|
||||||
if _, ok := d.configMaps[pathnme]; !ok {
|
if _, ok := d.configMaps[pathnme]; !ok {
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import (
|
|||||||
"katenary.io/internal/generator/labels"
|
"katenary.io/internal/generator/labels"
|
||||||
|
|
||||||
yaml3 "gopkg.in/yaml.v3"
|
yaml3 "gopkg.in/yaml.v3"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -643,3 +645,197 @@ services:
|
|||||||
t.Errorf("Expected command to be 'bar baz', got %s", strings.Join(command, " "))
|
t.Errorf("Expected command to be 'bar baz', got %s", strings.Join(command, " "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRestrictedRBACGeneration(t *testing.T) {
|
||||||
|
composeFile := `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx:1.29
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
|
||||||
|
database:
|
||||||
|
image: mariadb:10.5
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
`
|
||||||
|
tmpDir := setup(composeFile)
|
||||||
|
defer teardown(tmpDir)
|
||||||
|
|
||||||
|
currentDir, _ := os.Getwd()
|
||||||
|
os.Chdir(tmpDir)
|
||||||
|
defer os.Chdir(currentDir)
|
||||||
|
|
||||||
|
rbacOutput := internalCompileTest(t, "-s", "templates/web/depends-on.rbac.yaml")
|
||||||
|
|
||||||
|
docs := strings.Split(rbacOutput, "---\n")
|
||||||
|
|
||||||
|
// Filter out empty documents and strip helm template comments
|
||||||
|
var filteredDocs []string
|
||||||
|
for _, doc := range docs {
|
||||||
|
if strings.TrimSpace(doc) != "" {
|
||||||
|
// Remove '# Source:' comment lines that helm template adds
|
||||||
|
lines := strings.Split(doc, "\n")
|
||||||
|
var contentLines []string
|
||||||
|
for _, line := range lines {
|
||||||
|
if !strings.HasPrefix(strings.TrimSpace(line), "# Source:") {
|
||||||
|
contentLines = append(contentLines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filteredDocs = append(filteredDocs, strings.Join(contentLines, "\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filteredDocs) != 3 {
|
||||||
|
t.Fatalf("Expected 3 YAML documents in RBAC file, got %d (filtered from %d)", len(filteredDocs), len(docs))
|
||||||
|
}
|
||||||
|
|
||||||
|
var sa corev1.ServiceAccount
|
||||||
|
if err := yaml.Unmarshal([]byte(strings.TrimSpace(filteredDocs[0])), &sa); err != nil {
|
||||||
|
t.Errorf("Failed to unmarshal ServiceAccount: %v", err)
|
||||||
|
}
|
||||||
|
if sa.Kind != "ServiceAccount" {
|
||||||
|
t.Errorf("Expected Kind=ServiceAccount, got %s", sa.Kind)
|
||||||
|
}
|
||||||
|
if !strings.Contains(sa.Name, "web") {
|
||||||
|
t.Errorf("Expected ServiceAccount name to contain 'web', got %s", sa.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var role rbacv1.Role
|
||||||
|
if err := yaml.Unmarshal([]byte(strings.TrimSpace(filteredDocs[1])), &role); err != nil {
|
||||||
|
t.Errorf("Failed to unmarshal Role: %v", err)
|
||||||
|
}
|
||||||
|
if role.Kind != "Role" {
|
||||||
|
t.Errorf("Expected Kind=Role, got %s", role.Kind)
|
||||||
|
}
|
||||||
|
if len(role.Rules) != 1 {
|
||||||
|
t.Errorf("Expected 1 rule in Role, got %d", len(role.Rules))
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := role.Rules[0]
|
||||||
|
if !contains(rule.APIGroups, "") {
|
||||||
|
t.Error("Expected APIGroup to include core API ('')")
|
||||||
|
}
|
||||||
|
if !contains(rule.Resources, "endpoints") {
|
||||||
|
t.Errorf("Expected Resource to include 'endpoints', got %v", rule.Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, res := range rule.Resources {
|
||||||
|
if res == "*" {
|
||||||
|
t.Error("Role should not have wildcard (*) resource permissions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, verb := range rule.Verbs {
|
||||||
|
if verb == "*" {
|
||||||
|
t.Error("Role should not have wildcard (*) verb permissions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rb rbacv1.RoleBinding
|
||||||
|
if err := yaml.Unmarshal([]byte(strings.TrimSpace(filteredDocs[2])), &rb); err != nil {
|
||||||
|
t.Errorf("Failed to unmarshal RoleBinding: %v", err)
|
||||||
|
}
|
||||||
|
if rb.Kind != "RoleBinding" {
|
||||||
|
t.Errorf("Expected Kind=RoleBinding, got %s", rb.Kind)
|
||||||
|
}
|
||||||
|
if len(rb.Subjects) != 1 {
|
||||||
|
t.Errorf("Expected 1 subject in RoleBinding, got %d", len(rb.Subjects))
|
||||||
|
}
|
||||||
|
if rb.Subjects[0].Kind != "ServiceAccount" {
|
||||||
|
t.Errorf("Expected Subject Kind=ServiceAccount, got %s", rb.Subjects[0].Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helm template renders the name, so check if it contains "web"
|
||||||
|
if !strings.Contains(rb.RoleRef.Name, "web") {
|
||||||
|
t.Errorf("Expected RoleRef Name to contain 'web', got %s", rb.RoleRef.Name)
|
||||||
|
}
|
||||||
|
if rb.RoleRef.Kind != "Role" {
|
||||||
|
t.Errorf("Expected RoleRef Kind=Role, got %s", rb.RoleRef.Kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeploymentReferencesServiceAccount(t *testing.T) {
|
||||||
|
composeFile := `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx:1.29
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
|
||||||
|
database:
|
||||||
|
image: mariadb:10.5
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
`
|
||||||
|
tmpDir := setup(composeFile)
|
||||||
|
defer teardown(tmpDir)
|
||||||
|
|
||||||
|
currentDir, _ := os.Getwd()
|
||||||
|
os.Chdir(tmpDir)
|
||||||
|
defer os.Chdir(currentDir)
|
||||||
|
|
||||||
|
output := internalCompileTest(t, "-s", "templates/web/deployment.yaml")
|
||||||
|
|
||||||
|
var dt appsv1.Deployment
|
||||||
|
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
|
||||||
|
t.Errorf("Failed to unmarshal Deployment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccountName := dt.Spec.Template.Spec.ServiceAccountName
|
||||||
|
if !strings.Contains(serviceAccountName, "web") {
|
||||||
|
t.Errorf("Expected ServiceAccountName to contain 'web', got %s", serviceAccountName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dt.Spec.Template.Spec.InitContainers) == 0 {
|
||||||
|
t.Fatal("Expected at least one init container for depends_on")
|
||||||
|
}
|
||||||
|
|
||||||
|
initContainer := dt.Spec.Template.Spec.InitContainers[0]
|
||||||
|
if initContainer.Name != "wait-for-database" {
|
||||||
|
t.Errorf("Expected init container name 'wait-for-database', got %s", initContainer.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fullCommand := strings.Join(initContainer.Command, " ")
|
||||||
|
if !strings.Contains(fullCommand, "wget") {
|
||||||
|
t.Error("Expected init container to use wget for K8s API calls")
|
||||||
|
}
|
||||||
|
if !strings.Contains(fullCommand, "/api/v1/namespaces/") {
|
||||||
|
t.Error("Expected init container to call /api/v1/namespaces/ endpoint")
|
||||||
|
}
|
||||||
|
if !strings.Contains(fullCommand, "/endpoints/") {
|
||||||
|
t.Error("Expected init container to access /endpoints/ resource")
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNamespace := false
|
||||||
|
for _, env := range initContainer.Env {
|
||||||
|
if env.Name == "NAMESPACE" && env.ValueFrom != nil && env.ValueFrom.FieldRef != nil {
|
||||||
|
if env.ValueFrom.FieldRef.FieldPath == "metadata.namespace" {
|
||||||
|
hasNamespace = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasNamespace {
|
||||||
|
t.Error("Expected NAMESPACE env var with metadata.namespace fieldRef")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat("./chart/templates/web/depends-on.rbac.yaml")
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Error("RBAC file depends-on.rbac.yaml should exist for service using depends_on with K8s API")
|
||||||
|
} else if err != nil {
|
||||||
|
t.Errorf("Unexpected error checking RBAC file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(slice []string, item string) bool {
|
||||||
|
for _, s := range slice {
|
||||||
|
if s == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
// The Generate function will create the HelmChart object this way:
|
// The Generate function will create the HelmChart object this way:
|
||||||
//
|
//
|
||||||
// - Detect the service port name or leave the port number if not found.
|
// - Detect the service port name or leave the port number if not found.
|
||||||
// - Create a deployment for each service that are not ingnore.
|
// - Create a deployment for each service that are not ingore.
|
||||||
// - Create a service and ingresses for each service that has ports and/or declared ingresses.
|
// - Create a service and ingresses for each service that has ports and/or declared ingresses.
|
||||||
// - Create a PVC or Configmap volumes for each volume.
|
// - Create a PVC or Configmap volumes for each volume.
|
||||||
// - Create init containers for each service which has dependencies to other services.
|
// - Create init containers for each service which has dependencies to other services.
|
||||||
@@ -134,6 +134,12 @@ func Generate(project *types.Project) (*HelmChart, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set ServiceAccountName for deployments that need it
|
||||||
|
for _, d := range deployments {
|
||||||
|
d.SetServiceAccountName()
|
||||||
|
}
|
||||||
|
|
||||||
for _, name := range drops {
|
for _, name := range drops {
|
||||||
delete(deployments, name)
|
delete(deployments, name)
|
||||||
}
|
}
|
||||||
@@ -142,6 +148,11 @@ func Generate(project *types.Project) (*HelmChart, error) {
|
|||||||
chart.setEnvironmentValuesFrom(s, deployments)
|
chart.setEnvironmentValuesFrom(s, deployments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate RBAC resources for services that need K8s API access (non-legacy depends_on)
|
||||||
|
if err := chart.generateRBAC(deployments); err != nil {
|
||||||
|
logger.Fatalf("error generating RBAC: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// generate configmaps with environment variables
|
// generate configmaps with environment variables
|
||||||
if err := chart.generateConfigMapsAndSecrets(project); err != nil {
|
if err := chart.generateConfigMapsAndSecrets(project); err != nil {
|
||||||
logger.Fatalf("error generating configmaps and secrets: %s", err)
|
logger.Fatalf("error generating configmaps and secrets: %s", err)
|
||||||
@@ -440,6 +451,58 @@ func samePodVolume(service types.ServiceConfig, v types.ServiceVolumeConfig, dep
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateRBAC creates RBAC resources (ServiceAccount, Role, RoleBinding) for services that need K8s API access.
|
||||||
|
// A service needs RBAC if it has non-legacy depends_on relationships.
|
||||||
|
func (chart *HelmChart) generateRBAC(deployments map[string]*Deployment) error {
|
||||||
|
serviceMap := make(map[string]bool)
|
||||||
|
|
||||||
|
for _, d := range deployments {
|
||||||
|
if !d.needsServiceAccount {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sa := NewServiceAccount(*d.service, chart.Name)
|
||||||
|
role := NewRestrictedRole(*d.service, chart.Name)
|
||||||
|
rb := NewRestrictedRoleBinding(*d.service, chart.Name)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
saYaml, err := yaml.Marshal(sa.ServiceAccount)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshaling ServiceAccount for %s: %w", d.service.Name, err)
|
||||||
|
}
|
||||||
|
buf.Write(saYaml)
|
||||||
|
buf.WriteString("---\n")
|
||||||
|
|
||||||
|
roleYaml, err := yaml.Marshal(role.Role)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshaling Role for %s: %w", d.service.Name, err)
|
||||||
|
}
|
||||||
|
buf.Write(roleYaml)
|
||||||
|
buf.WriteString("---\n")
|
||||||
|
|
||||||
|
rbYaml, err := yaml.Marshal(rb.RoleBinding)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshaling RoleBinding for %s: %w", d.service.Name, err)
|
||||||
|
}
|
||||||
|
buf.Write(rbYaml)
|
||||||
|
|
||||||
|
filename := d.service.Name + "/depends-on.rbac.yaml"
|
||||||
|
chart.Templates[filename] = &ChartTemplate{
|
||||||
|
Content: buf.Bytes(),
|
||||||
|
Servicename: d.service.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceMap[d.service.Name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for svcName := range serviceMap {
|
||||||
|
logger.Log(logger.IconPackage, "Creating RBAC", svcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func fixContainerNames(project *types.Project) {
|
func fixContainerNames(project *types.Project) {
|
||||||
// fix container names to be unique
|
// fix container names to be unique
|
||||||
for i, service := range project.Services {
|
for i, service := range project.Services {
|
||||||
|
|||||||
@@ -128,6 +128,79 @@ func (r *Role) Yaml() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewServiceAccount creates a new ServiceAccount from a compose service.
|
||||||
|
func NewServiceAccount(service types.ServiceConfig, appName string) *ServiceAccount {
|
||||||
|
return &ServiceAccount{
|
||||||
|
ServiceAccount: &corev1.ServiceAccount{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: utils.TplName(service.Name, appName),
|
||||||
|
Labels: GetLabels(service.Name, appName),
|
||||||
|
Annotations: Annotations,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
service: &service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRestrictedRole creates a Role with minimal permissions for init containers.
|
||||||
|
func NewRestrictedRole(service types.ServiceConfig, appName string) *Role {
|
||||||
|
return &Role{
|
||||||
|
Role: &rbacv1.Role{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "Role",
|
||||||
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: utils.TplName(service.Name, appName),
|
||||||
|
Labels: GetLabels(service.Name, appName),
|
||||||
|
Annotations: Annotations,
|
||||||
|
},
|
||||||
|
Rules: []rbacv1.PolicyRule{
|
||||||
|
{
|
||||||
|
APIGroups: []string{""},
|
||||||
|
Resources: []string{"endpoints"},
|
||||||
|
Verbs: []string{"get", "list", "watch"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
service: &service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRestrictedRoleBinding creates a RoleBinding that binds the restricted role to the ServiceAccount.
|
||||||
|
func NewRestrictedRoleBinding(service types.ServiceConfig, appName string) *RoleBinding {
|
||||||
|
return &RoleBinding{
|
||||||
|
RoleBinding: &rbacv1.RoleBinding{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "RoleBinding",
|
||||||
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: utils.TplName(service.Name, appName),
|
||||||
|
Labels: GetLabels(service.Name, appName),
|
||||||
|
Annotations: Annotations,
|
||||||
|
},
|
||||||
|
Subjects: []rbacv1.Subject{
|
||||||
|
{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
Name: utils.TplName(service.Name, appName),
|
||||||
|
Namespace: "{{ .Release.Namespace }}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RoleRef: rbacv1.RoleRef{
|
||||||
|
Kind: "Role",
|
||||||
|
Name: utils.TplName(service.Name, appName),
|
||||||
|
APIGroup: "rbac.authorization.k8s.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
service: &service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceAccount is a kubernetes ServiceAccount.
|
// ServiceAccount is a kubernetes ServiceAccount.
|
||||||
type ServiceAccount struct {
|
type ServiceAccount struct {
|
||||||
*corev1.ServiceAccount
|
*corev1.ServiceAccount
|
||||||
|
|||||||
Reference in New Issue
Block a user