Skip to content

feat(controller,api): Implement break-the-glass functionality#1959

Open
bakito wants to merge 6 commits into
projectcapsule:mainfrom
bakito:break-the-glass
Open

feat(controller,api): Implement break-the-glass functionality#1959
bakito wants to merge 6 commits into
projectcapsule:mainfrom
bakito:break-the-glass

Conversation

@bakito

@bakito bakito commented Jun 9, 2026

Copy link
Copy Markdown
Contributor
  • api
  • controller
  • move cmd -> cmd/controller
  • cli
  • webhook
  • github workflows
  • e2e tests
  • how to handle rbac needed to manage the resources from BreakRequest Templates? -> no will be solved with inpersonation
  • add a feature flag? -> no
  • allow empty duration to keep the resource forever
  • correct docs/references
  • One schema for params
  • templates in slice?
  • server side apply
  • move template validation to pkg / template
  • service account by template

Open points to discuss:

  • use different template engine?
  • should template items trigger break request reconcile?
  • how to structure the new cli? -> break / btg

@bakito
bakito force-pushed the break-the-glass branch 10 times, most recently from 6043d3a to 95e6c74 Compare June 9, 2026 18:07
@bakito bakito changed the title feat: Implement break-the-glass functionality feat(controller,api): Implement break-the-glass functionality Jun 9, 2026
@bakito
bakito force-pushed the break-the-glass branch 2 times, most recently from 2932d10 to 9306b18 Compare June 9, 2026 18:13
@bakito
bakito force-pushed the break-the-glass branch 12 times, most recently from 19cb9c9 to 49f0f89 Compare June 12, 2026 11:16
@bakito
bakito force-pushed the break-the-glass branch 4 times, most recently from 4a6e154 to 538c2c6 Compare June 28, 2026 11:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread internal/webhook/breaktheglass/breakrequest_validating_test.go
Comment thread api/v1beta2/breakrequesttemplate_types.go Outdated
Comment thread internal/metrics/breakrequest_recorder.go
Comment thread internal/breaktheglass/template/schema.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread internal/webhook/test/webhook.go
Comment thread internal/controllers/breaktheglass/breakrequest_controller.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated
Comment thread internal/webhook/breaktheglass/breakrequesttemplate_validating.go
Comment thread internal/controllers/breaktheglass/breakrequest_controller.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated
Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread internal/controllers/breaktheglass/breakrequest_controller.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated
Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread cmd/cli/cmd/utils.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread api/v1beta2/breakrequest_func.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread api/v1beta2/breakrequest_func.go
Comment thread api/v1beta2/breakrequest_func.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated
Comment thread api/v1beta2/breakrequest_func.go
Comment thread internal/controllers/breaktheglass/breakrequest_controller.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 46 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread internal/webhook/breaktheglass/breakrequest_validating.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 5 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated
Comment thread pkg/api/breaktheglass/extended-duration_types.go Outdated
Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread api/v1beta2/breakrequest_func_test.go
Comment thread api/v1beta2/breakrequest_func.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread e2e/breaktheglass_test.go Outdated
Comment thread internal/webhook/breaktheglass/breakrequest_validating_test.go
Comment on lines +415 to +419
// Apply the object to the cluster
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, obj, func() error {
// CreateOrUpdate re-fetches the live object before running this function, so any
// metadata that must be enforced needs to be applied here as well.
if !br.Status.Active.ActiveUntil.IsZero() {

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread internal/controllers/breaktheglass/breakrequest_controller.go Outdated
Comment thread api/v1beta2/breakrequest_func.go
Comment thread api/v1beta2/breakrequest_func.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread api/v1beta2/breakrequest_func.go
Comment thread api/v1beta2/breakrequest_func.go Outdated
Comment thread internal/controllers/breaktheglass/breakrequest_controller.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment on lines +309 to +311
if br.Status.KeepUntil.Time.IsZero() || time.Until(br.Status.KeepUntil.Time) <= 0 {
return nil
}
Comment thread internal/controllers/breaktheglass/breakrequest_controller.go
Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment thread pkg/api/breaktheglass/zz_generated.deepcopy.go Outdated
Comment thread cmd/cli/cmd/utils.go
Comment on lines +53 to +60
switch {
case app.Duration != nil && app.Duration.Duration != 0:
effectiveDurationStr = app.Duration.Duration.String()
case br.Spec.Duration != nil && br.Spec.Duration.Duration != 0:
effectiveDurationStr = br.Spec.Duration.Duration.String()
case br.Status.Template != nil && br.Status.Template.DefaultDuration != nil && br.Status.Template.DefaultDuration.Duration != 0:
effectiveDurationStr = br.Status.Template.DefaultDuration.Duration.String()
}
Comment on lines +43 to +45
Items: breaktheglass.TemplateItems{
"cm": {ManifestTemplate: runtime.RawExtension{Object: &corev1.ConfigMap{}}},
},
Comment thread api/v1beta2/breakrequest_types.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment on lines +8 to +11
package breaktheglass

import ()

Comment thread cmd/cli/cmd/utils.go
Comment on lines +53 to +60
switch {
case app.Duration != nil && app.Duration.Duration != 0:
effectiveDurationStr = app.Duration.Duration.String()
case br.Spec.Duration != nil && br.Spec.Duration.Duration != 0:
effectiveDurationStr = br.Spec.Duration.Duration.String()
case br.Status.Template != nil && br.Status.Template.DefaultDuration != nil && br.Status.Template.DefaultDuration.Duration != 0:
effectiveDurationStr = br.Status.Template.DefaultDuration.Duration.String()
}
Comment on lines +69 to +72
) (err error) {
if reason == "" {
reason = "Access request approved"
}
Signed-off-by: bakito <github@bakito.ch>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment on lines +124 to +128
func (br *BreakRequest) ActiveRequest(entity *breaktheglass.AccessEntity) (err error) {
now := metav1.Now()

if err := br.transitionRequestPhase(
RequestPhaseActive,
Comment on lines +294 to +299
// Prevent duplicate condition entries of the same type
for _, cond := range br.Status.Conditions {
if RequestPhase(cond.Type) == newPhase {
return nil
}
}
Comment thread DEVELOPMENT.md
Comment on lines 66 to +67
# Create a KinD cluster if not already created
$ make dev-cluster
$ make dev-build dev-install-deps
Signed-off-by: bakito <github@bakito.ch>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 46 out of 48 changed files in this pull request and generated 7 comments.

Files not reviewed (1)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file

Comment on lines +75 to +79
mocks: func(cl *mc.MockClient, scl *mc.MockSubResourceWriter) {
cl.EXPECT().Get(gm.Any(), gm.Any(), matchBr).Return(nil)
cl.EXPECT().Get(gm.Any(), gm.Any(), matchBrt).Return(nil)
scl.EXPECT().Update(gm.Any(), matchBr, gm.Any()).Return(nil)
},
Comment on lines +111 to +114
mocks: func(cl *mc.MockClient, scl *mc.MockSubResourceWriter) {
cl.EXPECT().Get(gm.Any(), gm.Any(), matchBr).Return(nil)
scl.EXPECT().Update(gm.Any(), matchBr, gm.Any()).Return(nil)
},
Comment on lines +162 to +167
mocks: func(cl *mc.MockClient, scl *mc.MockSubResourceWriter) {
cl.EXPECT().Get(gm.Any(), gm.Any(), matchBr).Return(nil)
cl.EXPECT().Get(gm.Any(), gm.Any(), matchUs).Return(nil)
cl.EXPECT().Update(gm.Any(), matchUs, gm.Any()).Return(nil)
scl.EXPECT().Update(gm.Any(), matchBr, gm.Any()).Return(nil)
},
Comment on lines +37 to +40
Example: `
# interactive review
capsule review grant-admin --namespace default

Comment on lines +40 to +44

# non-interactive approve/deny
capsule review grant-admin --namespace default --approve
capsule review grant-admin --namespace default --deny
`,
Comment on lines +17 to +20
Example: `
# activate an existing BreakRequest
capsule activate grant-admin --namespace default
`,
Comment on lines +17 to +20
Example: `
# expire an existing BreakRequest
capsule expire grant-admin --namespace default
`,

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

bakito added 2 commits July 16, 2026 20:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 44 changed files in this pull request and generated 7 comments.

Files not reviewed (2)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file
  • internal/mocks/client/mock.go: Generated file

Comment on lines +416 to +420
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, obj, func() error {
// CreateOrUpdate re-fetches the live object before running this function, so any
// metadata that must be enforced needs to be applied here as well.
if !br.Status.Active.ActiveUntil.IsZero() {
ann := obj.GetAnnotations()
Comment on lines +128 to +145
if err := br.transitionRequestPhase(
RequestPhaseActive,
"Access request activated",
"ActivatedBySystem",
now,
entity,
); err != nil {
return err
}

if br.Status.Active == nil {
br.Status.Active = &ActivePeriod{}
}

tpl := br.Status.Template
if tpl == nil {
return fmt.Errorf("template not set")
}
Comment on lines +37 to +44
Example: `
# interactive review
capsule review grant-admin --namespace default

# non-interactive approve/deny
capsule review grant-admin --namespace default --approve
capsule review grant-admin --namespace default --deny
`,
Comment thread cmd/cli/cmd/breaktheglass/activate.go
Comment thread cmd/cli/cmd/breaktheglass/expire.go
Comment thread pkg/template/schema.go
Comment on lines +88 to +98
// For OAS 3.1: https://json-schema.org/draft/2020-12/schema
meta := "https://json-schema.org/draft/2020-12/schema"

c := jsonschema.NewCompiler()
if err := c.AddResource(
"meta.json",
bytes.NewReader([]byte(`{"$ref":"`+meta+`"}`)),
); err != nil {
return err
}
// Compile the candidate schema using the chosen meta-schema
Comment on lines +93 to +104
func y2j(in string) []byte {
m := make(map[string]any)
err := yaml.Unmarshal([]byte(in), &m)
if err != nil {
panic(err)
}
b, err := json.Marshal(m)
if err != nil {
panic(err)
}
return b
}

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 44 changed files in this pull request and generated 4 comments.

Files not reviewed (2)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file
  • internal/mocks/client/mock.go: Generated file

Comment on lines +125 to +146
func (br *BreakRequest) ActiveRequest(entity *breaktheglass.AccessEntity) (err error) {
now := metav1.Now()

if err := br.transitionRequestPhase(
RequestPhaseActive,
"Access request activated",
"ActivatedBySystem",
now,
entity,
); err != nil {
return err
}

if br.Status.Active == nil {
br.Status.Active = &ActivePeriod{}
}

tpl := br.Status.Template
if tpl == nil {
return fmt.Errorf("template not set")
}

Comment on lines +37 to +44
Example: `
# interactive review
capsule review grant-admin --namespace default

# non-interactive approve/deny
capsule review grant-admin --namespace default --approve
capsule review grant-admin --namespace default --deny
`,
Comment on lines +17 to +20
Example: `
# activate an existing BreakRequest
capsule activate grant-admin --namespace default
`,
Comment on lines +17 to +20
Example: `
# expire an existing BreakRequest
capsule expire grant-admin --namespace default
`,
Signed-off-by: bakito <github@bakito.ch>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 44 changed files in this pull request and generated 4 comments.

Files not reviewed (2)
  • api/v1beta2/zz_generated.deepcopy.go: Generated file
  • internal/mocks/client/mock.go: Generated file

Comment on lines +21 to +29
// InitializeFromTemplate Copies all relevant values from the Template.
func (br *BreakRequest) InitializeFromTemplate(brt *BreakRequestTemplate) {
br.Status.Template = &TemplateProperties{
Templates: brt.Spec.Templates,
DefaultDuration: brt.Spec.DefaultDuration,
MaxDuration: brt.Spec.MaxDuration,
KeepFor: brt.Spec.KeepFor,
}
}
Comment thread pkg/template/schema.go
Comment on lines +24 to +28
for i, tpl := range tis {
if _, err := validateTemplate(tpl.Raw); err != nil {
return fmt.Errorf("template %d is invalid: %w", i, err)
}
}
Comment on lines +37 to +44
Example: `
# interactive review
capsule break-the-glass review grant-admin --namespace default

# non-interactive approve/deny
capsule review grant-admin --namespace default --approve
capsule review grant-admin --namespace default --deny
`,
Comment on lines +124 to +145
// ActiveRequest Activates the BreakRequest, allowing the subject to access the requested resources.
func (br *BreakRequest) ActiveRequest(entity *breaktheglass.AccessEntity) (err error) {
now := metav1.Now()

if err := br.transitionRequestPhase(
RequestPhaseActive,
"Access request activated",
"ActivatedBySystem",
now,
entity,
); err != nil {
return err
}

if br.Status.Active == nil {
br.Status.Active = &ActivePeriod{}
}

tpl := br.Status.Template
if tpl == nil {
return fmt.Errorf("template not set")
}

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants