Skip to content
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

Improve the external authorization concept from opt-in to secure-by-default #3506

Conversation

haassConnyun
Copy link

@haassConnyun haassConnyun commented Dec 3, 2018

What this PR does / why we need it:
Assuming the cluster uses an external authorization service,
currently activation of external authorization is per ingress rule, that means the annotation auth-url needs to be added to every service that is publicly available. We call this security by opt-in.
To improve this concept to security-by-default be propose to add a parameter to the configmap which takes care of authorizing every request automatically.

The existing no-auth-locations is used to manually whitelist services, the new code creates the needed internal location and adds a sub-auth request to every regular location, just as happens with the auth-url annotation

@k8s-ci-robot
Copy link
Contributor

Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please follow instructions at https://git.k8s.io/community/CLA.md#the-contributor-license-agreement to sign the CLA.

It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please sign in with your organization's credentials at https://identity.linuxfoundation.org/projects/cncf to be authorized.
  • If you have done the above and are still having issues with the CLA being reported as unsigned, please email the CNCF helpdesk: [email protected]

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@k8s-ci-robot k8s-ci-robot added cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Dec 3, 2018
@haassConnyun
Copy link
Author

CLA signed

1 similar comment
@okryvoshapka-connyun
Copy link
Contributor

CLA signed

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels Dec 4, 2018
@haassConnyun
Copy link
Author

/assign @antoineco

@haassConnyun
Copy link
Author

/shrug

@k8s-ci-robot k8s-ci-robot added the ¯\_(ツ)_/¯ ¯\\\_(ツ)_/¯ label Dec 4, 2018
@okryvoshapka-connyun
Copy link
Contributor

PR raised from issue #3276

@haassConnyun
Copy link
Author

/unassign @antoineco
/unshrug
was following the bot's advise, apparently not always wanted. sorry.

@k8s-ci-robot
Copy link
Contributor

@haassConnyun: ¯\_(ツ)_/¯

In response to this:

/unassign @antoineco
/unshrug
was following the bot's advise, apparently not always wanted. sorry.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@k8s-ci-robot k8s-ci-robot removed the ¯\_(ツ)_/¯ ¯\\\_(ツ)_/¯ label Dec 5, 2018
@aledbf aledbf added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jan 5, 2019
@haassConnyun
Copy link
Author

@aledbf sorry to bump this - is there something we should do to get a review or a comment?

@ElvinEfendi
Copy link
Member

Sorry for delay, I'll try to review this by the end of this week.

@ElvinEfendi
Copy link
Member

Locations that should not get authenticated can be listed using no-auth-locations. See no-auth-locations

@haassConnyun no-auth-locations is a global setting, it won't let users to whitelist a path for a given service. Do you have any plan to implement something so that users can opt out of this global auth setting per ingress? Maybe something like auth-url-off annotation where when it's set to true it will not enable external auth.

glog.Warningf("Global auth location denied, reason: url scheme is empty")
} else if authURL.Host == "" {
glog.Warningf("Global auth location denied, reason: url host is empty")
} else if strings.Contains(authURL.Host, "..") {
Copy link
Member

Choose a reason for hiding this comment

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

Is this some kind of edge case where url.Parse does not err?

Copy link
Contributor

Choose a reason for hiding this comment

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

This was taken from auth-url annotation parser.
https://github.com/kubernetes/ingress-nginx/blob/master/internal/ingress/annotations/authreq/main.go#L137
The idea behind was to follow the syntax and style of the existing solution

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it is the case url.Parse does not catch.


authURL, err := url.Parse(val)
if err != nil {
glog.Warningf("Global auth location denied, reason: url is not")
Copy link
Member

Choose a reason for hiding this comment

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

The log message seems incomplete. Also can you include the err in the message as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

I assume it should be ".. is not valid"))
Yes, will append the err to the end.

@@ -150,6 +152,25 @@ func ReadConfig(src map[string]string) config.Configuration {
}
}

// Verify that the configured global auth is parsable as URL. if not, set the default value
Copy link
Member

Choose a reason for hiding this comment

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

Also can you extract this verification logic into a helper/util function and reuse it both here and in auth-url annotation parsing?

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

@@ -1163,6 +1164,18 @@ stream {
{{- end }}
{{ end }}

{{ if not (isLocationInLocationList $location $all.Cfg.NoAuthLocations) }}
Copy link
Member

Choose a reason for hiding this comment

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

What if both auth-url annotation and global setting is set? If I read this correctly it'll have duplicate auth request setup. We should make sure annotation overrides global setting.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, can do it. Was not sure about the priorities. Now it is clear.

@ElvinEfendi
Copy link
Member

@haassConnyun thanks for the PR! I've made few inline comments. On top of them I think Nginx template should not have separate rendering for global and annotation settings. When the setting is set you can create the annotation manually in the controller and make sure every (except the whitelisted ones) ingress has it. That way you'd not need to touch nginx.tmpl.

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Feb 25, 2019
@okryvoshapka-connyun
Copy link
Contributor

Hi @ElvinEfendi,

As requested changes were made:

  1. It is possible now to disable the globally configured auth with Ingress annotation enable-global-auth: false.
  2. The proper priority is kept: auth-url annotation has higher priority then global-auth-url configration if both are set.
  3. URL verification logic was extracted into separate function and used in annotation auth-url and controller's config global-auth-url.
  4. Modification to the tmpl.conf was minimized, the existing solution for external auth was reused.

To assist you, I have drawn 2 tables:

  • When enable-global-auth is enabled (default)
A auth-url global-auth-url enable-global-auth expected behavior
1 /a /b true Request goes to /a
2 /a none true Request goes to /a
3 none /b true Request goes to /b
4 none none true No external auth at all
  • When enable-global-auth is disabled
B auth-url global-auth-url enable-global-auth expected behavior
1 /a /b false Request goes to /a
2 /a none false Request goes to /a
3 none /b false No external auth at all
4 none none false No external auth at all

@okryvoshapka-connyun okryvoshapka-connyun force-pushed the feature/extrenal-auth-security-opt-out branch from b4c8c26 to e786a47 Compare February 27, 2019 21:31
@okryvoshapka-connyun
Copy link
Contributor

Hi @ElvinEfendi

Rebased, tested, green.
Sorry for delay, faced with some e2e-test environment and go-formatting issues after rebase.

@okryvoshapka-connyun okryvoshapka-connyun force-pushed the feature/extrenal-auth-security-opt-out branch from baff365 to 53713fe Compare March 1, 2019 09:35
@okryvoshapka-connyun
Copy link
Contributor

Hi @ElvinEfendi ,

I hope the change is not too dramatic.
So the solution was extended to be complete and fully mirror the existing external auth solution.
Nothing was changed from previously implemented logic, only minor renaming.
It contains all the addons implemented in external auth via Ingress Rule, naming concept was kept:

Annotation ConfigMap setting
auth-method global-auth-method
auth-signin global-auth-signin
auth-response-headers global-auth-response-headers
auth-request-redirect global-auth-request-redirect
auth-snippet global-auth-snippet

The switch between 2 solutions is full, so or you are using location specific external auth, or go with global external auth (including all addons in both cases). For example, it is not possible to have global-auth-url: /foo and global-auth-method: POST but for one location change only the nginx.ingress.kubernetes.io/auth-method: GET.

Modification of tmpl.conf template was reduced to minimum, existing external auth solution was reused to maximum.

Can I get another feedback round?

P.S. I am working on rebase.

@okryvoshapka-connyun okryvoshapka-connyun force-pushed the feature/extrenal-auth-security-opt-out branch from 28366dc to a16a12d Compare March 19, 2019 16:06
@ElvinEfendi
Copy link
Member

@aledbf any reason why do you not wanna merge this? (I saw you added do-not-merge above)

@okryvoshapka-connyun
Copy link
Contributor

Hi @ElvinEfendi and @aledbf
Can we assist you somehow to speed up the te review of this PR?

@aledbf
Copy link
Member

aledbf commented May 2, 2019

@okryvoshapka-connyun apologies for the delay. Please squash the commits.
After we merge the webhook PR I'll review this one

@okryvoshapka-connyun okryvoshapka-connyun force-pushed the feature/extrenal-auth-security-opt-out branch from d7800f8 to 1aae430 Compare May 3, 2019 07:42
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 3, 2019
@okryvoshapka-connyun okryvoshapka-connyun force-pushed the feature/extrenal-auth-security-opt-out branch from 1aae430 to edc66f0 Compare May 3, 2019 08:00
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 3, 2019
@okryvoshapka-connyun okryvoshapka-connyun force-pushed the feature/extrenal-auth-security-opt-out branch from edc66f0 to 8cc9afe Compare May 3, 2019 10:13
@okryvoshapka-connyun
Copy link
Contributor

@aledbf , rebased and squashed.

@@ -389,6 +390,14 @@ nginx.ingress.kubernetes.io/auth-snippet: |
!!! example
Please check the [external-auth](../../examples/auth/external-auth/README.md) example.

#### Global External Authentication

By default the controller redirects all requests to an existing service that provides authentication if `global-auth-url` is set in the NGINX ConfigMap. If you want to disable this behavior for that ingress, you can use ssl-redirect: "false" in the NGINX ConfigMap.
Copy link
Member

Choose a reason for hiding this comment

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

What does ssl-redirect has to do with global-auth-url?

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed.


defIPCIDR = append(defIPCIDR, "0.0.0.0/0")
defNginxStatusIpv4Whitelist = append(defNginxStatusIpv4Whitelist, "127.0.0.1")
defNginxStatusIpv6Whitelist = append(defNginxStatusIpv6Whitelist, "::1")
defProxyDeadlineDuration := time.Duration(5) * time.Second
degGlobalExternalAuth := GlobalExternalAuth{"", "", "", "", append(defResponseHeaders, ""), "", ""}
Copy link
Member

Choose a reason for hiding this comment

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

s/deg/def

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed.

@ElvinEfendi
Copy link
Member

The PR is looking good! I still think we can avoid touching nginx.tmpl as I mentioned in #3506 (comment).

The location objects are built in controller.go, and when building we assign the annotations, including external auth. In the code path why can you not check global external auth and set external auth of location to the global one if the annotation is not set?

Or even better in the external auth annotation, why don't you set default value to the global external auth value from the configmap? For an example you can check

func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) {
. You can see there that for example if proxy-connect-timeout is given as annotation then it will be used, otherwise its default/fallback value will be taken from configmap (you can trace GetDefaultBackend function to see that).

@okryvoshapka-connyun
Copy link
Contributor

@ElvinEfendi , thank you for feedback.
The change in nginx.tmpl was done intentionally because I simply have not found an example of functionality you mentioned with proxy/main.go)))
My intentions were:

  1. not to flood and corrupt the structure of annotations with imports from the controller or vice-versa.
  2. make the switch the between annotation and global configuration easily visible and not hidden in the code of annotation parser or controller parser.

As you have pointed to an example of "proxy-connect-timeout", I can change it, but it will take some time as I am a bit busy with another project.
Should I change?

P.S. After simply typos fixes, e2e tests have failed for an unclear reason No API token found for service account "ingress-nginx-e2e", retry after the token is automatically created and added to the service account. Any hint?)

@aledbf aledbf removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label May 7, 2019
@ElvinEfendi
Copy link
Member

/approve

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 7, 2019
@aledbf
Copy link
Member

aledbf commented May 7, 2019

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 7, 2019
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: aledbf, ElvinEfendi, haassConnyun

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants