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

WIP: ws: Add client certificate authentication #11421

Open
wants to merge 3 commits into
base: master
from

Conversation

@martinpitt
Copy link
Member

martinpitt commented Mar 18, 2019

Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

sd-bus was released in systemd 221, so bump the build dependency
accordingly. Drop the now obsolete fallback to the split-out libs, all
our supported OSes have a much newer systemd.

Introduce a new tls-cert authorization type into
cockpit-ws/cockpit-session that gets enabled with the new
ClientCertAuthentication option in cockpit.conf. If this is enabled,
and the client sends a TLS client certificate to cockpit-tls,
pam_cockpit_cert maps the certificate to a user name.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368

  • Add sssd-dbus to Debian/Ubuntu images: PR #11422
  • Add sssd-dbus to RHEL/CentOS images: PR #11423
  • document (only) in authentication.md
  • Consider whether to call pam_authenticate() with a conditionalized pam_unix -- let's not do that
  • Fix SELinux policy instead of disabling it, and report downstream bug -- scratch build with upstream fix
  • Update SELinux policy to allow memfds; downstream bug -- this can be reverted
  • Move cockpit-cert-session into a new binary package; don't ship that in releases
  • Check with stakeholder whether we need to validate the cert or leave that to sssd (that)
  • Fix formatting of authentication.md: PR #11445
  • Fix container rpm installation: PR #11446
  • Split out TLS termination: PR #12068
  • cockpit-tls: export validated certificates: PR #12501
  • cockpit-tls: Reject expired certificates: PR #12554
  • Run cockpit-tls and cockpit-ws in different security contexts: PR #12902, PR #12972
  • update SELinux policy to allow PAM module to read /run/tls/cockpit/: fedora-selinux/selinux-policy-contrib#130
  • Document changing https instance slice limits
  • Document cert auth in guide
  • replace per-fd files and glob cert lookup with flock or similar approach
  • revert .spec file hack for SELinux after landing ^, as globbing isn't needed any more

Non-blockers:

@martinpitt martinpitt added blocked and removed blocked labels Mar 18, 2019
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from 84a9a41 to 26128de Mar 19, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 19, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the other PAM
stacks for session, etc.)

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from 26128de to e050358 Mar 19, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 19, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the other PAM
stacks for session, etc.)

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Mar 19, 2019

Nice, tests now by and large work. On fedora-29 there is a (non-fatal) SELinux violation:

audit: type=1400 audit(1553006049.704:621): avc:  denied  { map } for  pid=8208 comm="pool" path="/etc/pki/ca-trust/source/README" dev="dm-0" ino=8538086 scontext=system_u:system_r:cockpit_ws_t:s0 tcontext=system_u:object_r:cert_t:s0 tclass=file permissive=0
audit: type=1400 audit(1553006049.710:622): avc:  denied  { map } for  pid=8208 comm="pool" path="/etc/pki/ca-trust/source/ipa.p11-kit" dev="dm-0" ino=9401424 scontext=system_u:system_r:cockpit_ws_t:s0 tcontext=system_u:object_r:cert_t:s0 tclass=file permissive=0
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from e050358 to 495831e Mar 19, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 19, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the other PAM
stacks for session, etc.)

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from 495831e to add0d8d Mar 20, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 20, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the other PAM
stacks for session, etc.)

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from add0d8d to 29a990e Mar 20, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 20, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the "session"
PAM stack).

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Mar 20, 2019

I quickly talked about the PAM stuff with Stef. I was previously on the fence between entirely skipping the auth PAM stage with TLS certificates, or conditionalize it to skip substack password-auth when using TLS termination. But the latter only makes things more brittle, and the auth stage should not really do anything else than password validation (in particular, postlogin also runs as part of session, as it should). So I dropped that TODO item and just updated the comment.

This is now ready for review. @stefwalter , I'd appreciate if you have some time to take a look.

@martinpitt martinpitt requested a review from stefwalter Mar 20, 2019
@martinpitt martinpitt changed the title WIP: ws: Client certificate authentication proof of concept ws: Introduce (yet unsafe) client certificate authentication Mar 20, 2019
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Mar 20, 2019

@stefwalter : Some background (also explained in JIRA): This is mostly for demoing, and unblocking development of other parts of the entire experience (like how to handle sudo authentication via smartcards, configuring the UI differently, etc.). We won't mention that in the release notes, and this deliberately isn't documented in the guide or manpages. This will happen once we have a secure way to do this (see jira task COCKPIT-369).

src/ws/cert-session.c Outdated Show resolved Hide resolved
src/ws/cert-session.c Outdated Show resolved Hide resolved
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from 29a990e to e62ebd1 Mar 21, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 21, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the "session"
PAM stack).

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

On Fedora/RHEL, ships this in a (possibly temporary) new package
cockpit-ws-cert-session, which only gets built for CI (`wip`), not for
releases. Don't ship it yet on Debian/Ubuntu.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Mar 21, 2019

@stefwalter : I reworked this to package the new binary into a new rpm, which only gets built for CI for now. For interested people it's easy enough to enable in the .spec by changing the build_tech_preview macro.

Copy link
Contributor

stefwalter left a comment

Some comments. No major bugs found. This has several expected security holes. But we need to mark them clearly.

What would the the impact of just keeping this on a feature branch be?

src/ws/cert-session.c Outdated Show resolved Hide resolved
src/ws/cert-session.c Outdated Show resolved Hide resolved
src/ws/cert-session.c Outdated Show resolved Hide resolved
src/common/cockpitwebserver.c Outdated Show resolved Hide resolved
src/ws/cert-session.c Outdated Show resolved Hide resolved
src/ws/cert-session.c Outdated Show resolved Hide resolved
src/ws/cockpitauth.c Outdated Show resolved Hide resolved
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Mar 21, 2019

What would the the impact of just keeping this on a feature branch be?

  • we don't automatically track the functionality of the lower levels (sssd-dbus, IPA CLI, etc.) during image refreshes; in particular, the development of the SELinux policy updates
  • this PR would need to be rebased every once in a while, to make sure it doesn't bitrot too much

So, nothing too serious really. With the "tech preview" package here it won't actually get shipped in releases, so landing this PR only has a minimal actual impact.

But I have no strong opinion either way. By far the most useful purpose of that PR is to make sure it works everywhere.

@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from e62ebd1 to c19ef52 Mar 21, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 21, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the "session"
PAM stack).

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

On Fedora/RHEL, ships this in a (possibly temporary) new package
cockpit-ws-cert-session, which only gets built for CI (`wip`), not for
releases. Don't ship it yet on Debian/Ubuntu.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt added the blocked label Mar 22, 2019
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from c19ef52 to 1630c12 Mar 22, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Mar 22, 2019
Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled and the client sends a TLS client
certificate, use the new `cockpit-cert-session` session command instead
of `cockpit-session`. This uses sssd's API to map a TLS certificate to a
user [1], instead of the PAM "auth" stack (but still uses the "session"
PAM stack).

This is currently unsafe: It trusts cockpit-ws to do the TLS
termination, in particular validating that the remote end really has the
corresponding private key. Other authentication methods
(cockpit-{session,ssh} don't have to trust ws and do the validation
themselves, but this is tricky to do with a TLS connection as it's not
possible to get back to the browser and ask for the certificate again.
This needs to be re-engineered more fundamentally. Until then this is a
proof of concept and remains undocumented in the man pages and guide.
The "Unsafe.." option name hints to that. Only document this in
authentication.md, which is just for developers and not shipped.

On Fedora/RHEL, ships this in a (possibly temporary) new package
cockpit-ws-cert-session, which only gets built for CI (`wip`), not for
releases. Don't ship it yet on Debian/Ubuntu.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from d08cc25 to 4162048 Aug 10, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Aug 10, 2019
Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

sd-bus was released in systemd 221, so bump the build dependency
accordingly. Drop the now obsolete fallback to the split-out libs, all
our supported OSes have a much newer systemd.

Introduce a new `tls-cert` authorization type into cockpit-ws that gets
enabled with the new `UnsafeClientCertAuthentication` option in
cockpit.conf. If this is enabled, and the client sends a TLS client
certificate to cockpit-tls, call the new `cockpit-cert-session` session
command instead of `cockpit-session`. pam_cockpit_cert will then map the
certificate to a user name.

This is currently unsafe, as cockpit-tls runs as the same system user as
cockpit-ws. Until this is fixed this is a proof of concept and remains
undocumented in the man pages and guide.  The "Unsafe.." option name
hints to that. Only document this in authentication.md, which is just
for developers and not shipped.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421

TODO:
 - merge cockpit-cert-session back into cockpit-session
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Aug 10, 2019

/me blows the dust away..

I reworked this to get the certificate from cockpit-tls instead of cockpit-ws, and rebased to current master. In the meantime, all my previous SELinux adjustments landed in Fedora (maybe not yet RHEL, tests will show), but there's a new violation due to accessing /run/cockpit/tls/. I also dropped the separate package, we now aim for a fully production ready solution for landing this PR.

@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from 4162048 to feea91e Aug 10, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Aug 10, 2019
Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

sd-bus was released in systemd 221, so bump the build dependency
accordingly. Drop the now obsolete fallback to the split-out libs, all
our supported OSes have a much newer systemd.

Introduce a new `tls-cert` authorization type into
cockpit-ws/cockpit-session that gets enabled with the new
`ClientCertAuthentication` option in cockpit.conf. If this is enabled,
and the client sends a TLS client certificate to cockpit-tls,
pam_cockpit_cert maps the certificate to a user name.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from feea91e to ee6557e Aug 10, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Aug 10, 2019
Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

sd-bus was released in systemd 221, so bump the build dependency
accordingly. Drop the now obsolete fallback to the split-out libs, all
our supported OSes have a much newer systemd.

Introduce a new `tls-cert` authorization type into
cockpit-ws/cockpit-session that gets enabled with the new
`ClientCertAuthentication` option in cockpit.conf. If this is enabled,
and the client sends a TLS client certificate to cockpit-tls,
pam_cockpit_cert maps the certificate to a user name.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt changed the title WIP: ws: Add (yet unsafe) client certificate authentication WIP: ws: Add client certificate authentication Aug 10, 2019
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from ee6557e to 943dbe9 Aug 11, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Aug 11, 2019
Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

sd-bus was released in systemd 221, so bump the build dependency
accordingly. Drop the now obsolete fallback to the split-out libs, all
our supported OSes have a much newer systemd.

Introduce a new `tls-cert` authorization type into
cockpit-ws/cockpit-session that gets enabled with the new
`ClientCertAuthentication` option in cockpit.conf. If this is enabled,
and the client sends a TLS client certificate to cockpit-tls,
pam_cockpit_cert maps the certificate to a user name.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Aug 11, 2019

Updated SELinux policy hack to work on rhel-8-1

@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from 943dbe9 to cbcab8a Aug 12, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Aug 12, 2019
Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

sd-bus was released in systemd 221, so bump the build dependency
accordingly. Drop the now obsolete fallback to the split-out libs, all
our supported OSes have a much newer systemd.

Introduce a new `tls-cert` authorization type into
cockpit-ws/cockpit-session that gets enabled with the new
`ClientCertAuthentication` option in cockpit.conf. If this is enabled,
and the client sends a TLS client certificate to cockpit-tls,
pam_cockpit_cert maps the certificate to a user name.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from cbcab8a to 5c4657a Aug 28, 2019
martinpitt added a commit to martinpitt/cockpit that referenced this pull request Aug 28, 2019
Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

sd-bus was released in systemd 221, so bump the build dependency
accordingly. Drop the now obsolete fallback to the split-out libs, all
our supported OSes have a much newer systemd.

Introduce a new `tls-cert` authorization type into
cockpit-ws/cockpit-session that gets enabled with the new
`ClientCertAuthentication` option in cockpit.conf. If this is enabled,
and the client sends a TLS client certificate to cockpit-tls,
pam_cockpit_cert maps the certificate to a user name.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

[1] https://www.freeipa.org/page/V4/User_Certificates

Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes cockpit-project#11421
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Oct 22, 2019

In the last days I rebased this (locally) and tried to update it to the recent systemdification. So this simple pid-based approach doesn't work any more, as cockpit-tls now doesn't fork off cockpit-ws by itself any more.

But let's think about this more generally -- Doing a brain dump here to be discussed with @allisonkarlitskaya. For authentication, cockpit-session needs to query cockpit-tls about "do you have a current certificate for $THIS cockpit-ws?". For that we need to find a way to robustly identify/authenticate a particular cockpit-ws instance (that is associated to a particular certificate) from both cockpit-tls and cockpit-session. We don't want to trust anything that cockpit-ws does or can influence, so we need to find some immutable property or artifact that it holds.

I see three possibilities for that:

cockpit-ws pid:

  • cockpit-ws can in principle mess with the pid; e. g. fork-bomb and try to catch a pid of a different cockpit-ws instance that just exited, before cockpit-tls notices up and invalidates its instance → cert map entry. So this isn't ideal.
  • trivial to query for cockpit-session (getppid())
  • hard to query for cockpit-tls; I first tried getsockopt(SO_PEERCRED), but that always just gives "pid 1" as it returns the credentials at socket creation time, not the current holder. We don't want to use SCM_CREDENTIALS as that would require cockpit-ws'es cooperation and thus it's not trustworthy.
  • trivial to query for cockpit-ws; that also needs to do that mapping to find out whether cockpit-tls has a certificate (to decide whether it should use tls-cert or basic auth)

instance unix socket name

  • trivial for cockpit-tls, it already knows the name
  • possible to query from cockpit-session via /proc/self/cgroup and parsing the instance name; not entirely trivial as we need to work with cgroupv1 as well, but bearable.
  • cockpit-ws has no way to change its cgroup, so very reliable
  • needs some factorization to do the same query in cockpit-ws

pass an immutable token:
This could be a sealed memfd, like in an earlier approach of this PoC: cockpit-tls put the client cert into a memfd, passed it to cockpit-ws (via simple fork()), and cockpit-ws presented the fd to cockpit-session in the authorize message. cockpit-session validates that the owner of the fd is cockpit-tls (so that it's legit).

That earlier approach has two drawbacks: (1) it lacks validation for the token still being recent -- cockpit-ws could park the fd somewhere else and re-present it long after the user session ended. So it still needs to go back to query cockpit-tls whether $THIS cockpit-ws really still has the given certificate, at which point the $THIS identification problem is back -- plus, reliably identifying $THIS is already sufficient for the validation, so we don't really need the token.

  • cockpit-ws has no way to change it, so very reliable; however, it must be guarded against replay attacks
  • possible for cockpit-tls; it needs a way to send the token fd to cockpit-ws, possibly through adding a new --receive-token-fd option; ws would read it as the first thing after startup
  • trivial for cockpit-ws, the token gets passed through it

So all things considered, I think the cgroup approach is good enough while still being easy enough to implement.

martinpitt added 3 commits Mar 14, 2019
Add a new pam_cockpit_cert PAM module for TLS client certificate based
authentication. This uses sssd's API to map a TLS certificate to a
user [1]. Use the sd-bus API for making the D-Bus calls, instead of
gdbus (glib/gio has too many side effects, which should be avoided in a
setuid root program) or libdbus (which would be a new dependency).

Introduce a new `tls-cert` authorization type into
cockpit-ws/cockpit-session that gets enabled with the new
`ClientCertAuthentication` option in cockpit.conf. If this is enabled,
and cockpit-tls exports a current TLS client certificate,
pam_cockpit_cert maps the certificate to a user name.

Unfortunately the Chrome Devtools Protocol does not currently offer
client certificate import and selecting one for a page that requests a
certificate. Test this with curl instead, and describe in the comments
how to test this interactively.

This requires some more SELinux policy modifications. Apply them locally
in cockpit.spec until they land in Fedora/RHEL.

[1] https://www.freeipa.org/page/V4/User_Certificates

Fixes #8429
Related: https://bugzilla.redhat.com/show_bug.cgi?id=1678465
Jira: COCKPIT-368
Closes #11421
Bring back the export of client certificates for current connections
into the runtime directory. This got dropped in commit 7086711.

Export them to `$RUNTIME_DIRECTORY` under their peer certificate
fingerprint name. That can be derived from the systemd unit (cgroup) in
which cockpit-ws and cockpit-session run. To avoid an error prone
reference counting, just let each connection write its own file with a
unique thread id suffix. This could possibly be improved later with some
more clever solution with flock() or similar.

Each exported certificate file will only last as long as its
corresponding connection.  The authentication stack can thus rely on
certificates belonging to a current Cockpit session -- at least the
websocket connection will be open all the time.

If cockpit-tls itself quits, systemd will clean out the
RuntimeDirectory= for us, to avoid getting stale data.

Drop the restricted RuntimeDirectory permissions, so that they use the
default 0755. This allows the unprivileged cockpit-ws to check if there
is a client certificate, and thus if it should request authentication
with that.
See https://bugzilla.redhat.com/show_bug.cgi?id=1770159

This needs to be fixed properly before we can truly announce/support
this, but this workaround at least unblocks testing.
@martinpitt martinpitt force-pushed the martinpitt:client-cert-auth branch from 5c4657a to df17aaa Nov 11, 2019
@martinpitt

This comment has been minimized.

Copy link
Member Author

martinpitt commented Nov 11, 2019

I pushed another big rebase/rework against current master: this now uses cockpit-session's cgroup to identify the corresponding cockpit-wsintance-https@ instance name and thus the client certificate in /run/cockpit/tls/. It also adds some new SELinux policy, and adds a workaround to a FreeIPA regression in Fedora wrt. certificate lookup.

I'd like to discuss that approach with @allisonkarlitskaya: This approach epxorts one cert file for each thread. This is very robust and avoids refcounting, but requires globbing on the consumer side, and it's slightly odd to have many cert files with exactly the same content. An alternative approach may involve using flock(2) and just writing one file.

@martinpitt martinpitt added needswork and removed blocked labels Nov 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.