New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kubernetes security header annotations #2460
Kubernetes security header annotations #2460
Conversation
@dtomcej to fix the "gen pb" could you rebase your branch? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know the PR isn't ready yet, but I thought it could be worthwhile to give some early feedback, especially if we want this to still make it into 1.5. Hope you don't mind!
provider/kubernetes/kubernetes.go
Outdated
func getSliceAnnotation(meta v1.ObjectMeta, name string) []string { | ||
value := []string{} | ||
if annotation := meta.Annotations[name]; annotation != "" { | ||
for _, subannotation := range strings.Split(annotation, ",") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'm not sure if the name subannotation
is ideal. To me, it gives a fault sense of an annotation (that is, a map) inside an existing annotation. IIRC correctly, however, it's just a list of values.
How about calling the variable value
then (and adjust the log message below accordingly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
Subannotation was a carry over from the docker where labels get clumped together.
provider/kubernetes/kubernetes.go
Outdated
if annotation := meta.Annotations[name]; annotation != "" { | ||
for _, subannotation := range strings.Split(annotation, ",") { | ||
if len(subannotation) == 0 { | ||
log.Warnf("Could not load annotation %v, skipping", subannotation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the message should be more specific and about the fact we couldn't find any values for a known annotation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
provider/kubernetes/kubernetes.go
Outdated
} | ||
} | ||
if len(value) == 0 { | ||
log.Warnf("Could not load %v annotation", name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The skipping part is missing here.
provider/kubernetes/kubernetes.go
Outdated
} | ||
if len(value) == 0 { | ||
log.Warnf("Could not load %v annotation", name) | ||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have to be conscious about security-critical cases where missing header value should lead to the whole Ingress being skipped? I'm thinking of the equivalent of missing basic auth credentials that shouldn't lead to a frontend being accessible without authentication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing here really prevents code from loading, just appends or changes headers.
provider/kubernetes/kubernetes.go
Outdated
return nil | ||
} | ||
return value | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file is starting to get cluttered. How about moving those little helpers into a helpers.go
file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please no helpers/utils.go
file. In test ok but not in the prod code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No disagreement in code reviews without an alternative suggestion, they say.
What do you suggest instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, annotations_parser.go
or annotations.go
by example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do! 👍
provider/kubernetes/kubernetes.go
Outdated
value := []string{} | ||
if annotation := meta.Annotations[name]; annotation != "" { | ||
for _, subannotation := range strings.Split(annotation, ",") { | ||
if len(subannotation) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not entirely sure, but doesn't Split
on an empty string yield the empty string, i.e., ""
? That would still be non-zero in terms of the length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow. Did not know that. Will update accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh so in this case, its to catch empty sub-elements (such as x,y,,z
). The empty test is on the line above :)
9a5d445
to
cb9c79c
Compare
@ldez Rebased! |
09f35c3
to
a0315a9
Compare
templates/kubernetes.tmpl
Outdated
[frontends."{{$frontendName}}".headers] | ||
SSLRedirect = {{$frontend.Headers.SSLRedirect}} | ||
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}} | ||
SSLHost = "{{$frontend.Headers.SSLHost}}"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
small typo: double double quote 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eagle eyes
dea3558
to
ff9947d
Compare
7f67d54
to
1431e26
Compare
@@ -53,7 +73,7 @@ type Provider struct { | |||
lastConfiguration safe.Safe | |||
} | |||
|
|||
func (p *Provider) newK8sClient() (Client, error) { | |||
func (p Provider) newK8sClient() (Client, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want a copy of Provider
when calling newK8sClient
now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't reload the static configuration (eg we don't recreate the client).
NewInClusterClient(endpoint string)
and NewExternalClusterClient(endpoint, token, caFilePath string)
use string, a pointer as receiver don't change anything.
The use of a pointer as a receiver must be reserved only when a method changes the state of the receiver. (use a pointer as a receiver produce an escape to heap)
Maybe I have miss something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Late to the game, sorry.
The problem with receivers in Go is that they conflate two aspects: Mutability and performance / copy overhead. The Go Code Review Comments have a paragraph on the subject, but eventually suggest to leans towards pointer receivers, especially when the involved receiver is a non-trivial struct. I certainly think that Provider
is rather complex (especially since it embeds another BaseProvider
), so a pointer receiver would have been in order for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👏
add anno list add remove forcessl add k8s headers change error to warn log change empty maps to nil woops durp quote strings fix autogen update value/logging lol double quote move parsing template out empty maps ad docs
3595be6
to
23addba
Compare
This PR implements the secure headers middleware from kubernetes annotations. This PR follows up #2334 and #2146.