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

Unsufficient check in credentials.pm #7246

Closed
salben21 opened this issue Sep 5, 2022 · 9 comments · Fixed by #7247
Closed

Unsufficient check in credentials.pm #7246

salben21 opened this issue Sep 5, 2022 · 9 comments · Fixed by #7247
Assignees
Milestone

Comments

@salben21
Copy link

salben21 commented Sep 5, 2022

Hi,
I recognize that one can request the ssh_root_key's of every zone from every client independently of its zone affiliation by a simple script using getcredentials.awk and allowcred.awk. This is a security issue if you use multiple zones with sshbetweenodes=no to avoid a privilege escalation to other nodes.

It seems that in credentials.pm of the xcatd following checks are missing:

If a ssh_root_key was requested check

  • if the client is in the zone of the requested key's zone otherwise reject the request.
  • if the sshbetweennodes is set to yes in the requested zone, otherwise reject the request

best

@salben21
Copy link
Author

salben21 commented Sep 6, 2022

Workaround for xCAT-server/lib/xcat/plugins/credentials.pm from my side

    if ((($parm =~ /^ssh_root_key/) || ($parm =~ /^ssh_root_pub_key/)) && ($foundkeys == 0)) {
        my ($rootkeyparm, $zonename) = split(/:/, $parm);
        my $client_zonename=xCAT::Zone->getmyzonename($client);
        
        if ($zonename) {
            $parm = $rootkeyparm;           # take the zone off
            xCAT::MsgUtils->trace(0, 'I', "credentials: The node ($client) is asking for sshkeys of zone: $zonename.");
            if ($client_zonename eq $zonename) {
              my $sshbetweenodes_allowed = xCAT::Zone->enableSSHbetweennodes($client);
              if (($sshbetweenodes_allowed == 1) || ($parm =~ /^ssh_root_pub_key/)) { # check if sshbetweennodes is allowed or pub key is requested
                $sshrootkeydir = xCAT::Zone->getzonekeydir($zonename);
                if ($sshrootkeydir == 1) {      # error return
                    xCAT::MsgUtils->trace(0, 'W', "credentials: The zone: $zonename is not defined.");
                } else {
                    $foundkeys = 1;    # don't want to read the zone data twice
                }
              } else {
                  xCAT::MsgUtils->trace(0, 'E', "credentials: Not allowed to read root's private ssh key because sshbetweennodes is disabled.");
                  $sshrootkeydir = 1;
              }
            } else {
                xCAT::MsgUtils->trace(0, 'E', "credentials: Not allowed to read root's private ssh key of different zone.");
                $sshrootkeydir = 1;
            }
        } elsif ($client_zonename) { # check if no zonename is submitted but node is in a zone
            xCAT::MsgUtils->trace(0, 'E', "credentials: Not allowed to read root's private ssh key of default zone.");
            $sshrootkeydir = 1;
        }
    }

instead of

    if ((($parm =~ /^ssh_root_key/) || ($parm =~ /^ssh_root_pub_key/)) && ($foundkeys == 0)) {
        my ($rootkeyparm, $zonename) = split(/:/, $parm);
        if ($zonename) {
            $parm = $rootkeyparm;           # take the zone off
            xCAT::MsgUtils->trace(0, 'I', "credentials: The node ($client) is asking for sshkeys of zone: $zonename.");
            $sshrootkeydir = xCAT::Zone->getzonekeydir($zonename);
            if ($sshrootkeydir == 1) {      # error return
                xCAT::MsgUtils->trace(0, 'W', "credentials: The zone: $zonename is not defined.");
            } else {
                $foundkeys = 1;    # don't want to read the zone data twice
            }
        }
    }

@salben21
Copy link
Author

salben21 commented Sep 7, 2022

By the way: The argument in the description of the function enableSSHbetweennodes in the file [https://github.com/xcat2/xcat-core/tree/master/perl-xCAT/xCAT/Zone.pm] is nodename and not zonename

=head3 enableSSHbetweennodes
Arguments:
zonenamenodename
Returns:
1 if the sshbetweennodes attribute is yes/1 or undefined
0 if the sshbetweennodes attribute is no/0
Example:
xCAT::Zone->enableSSHbetweennodes($zonename$nodename);
=cut

@jjohnson42
Copy link
Member

BTW, I would be interested on your feedback on the confluent ssh design.

In confluent, no user private keys for root or any other user are ever shared. Same for private host keys, nodes generate their own.

Instead, confluent grants host certificates, sets up host based authentication based on the host certificates, and uses shosts.equiv to implement a variant of 'zones' (in confluent, it's "trustnodes", meaning that you can have asymettric zoning where, for example, the compute could trust the storage without storage trusting the compute).

Further, it implements a context aware where once a node security token is granted, it locks down the interface so that it can't be used, and getting ssh certs requires the initial token.

@jjohnson42
Copy link
Member

Oh, and you may want to make this a pull request, it seems straightforward enough to me.

@salben21
Copy link
Author

salben21 commented Sep 9, 2022 via email

@salben21
Copy link
Author

salben21 commented Sep 9, 2022

Here the latest release of the changes:

    if ((($parm =~ /^ssh_root_key/) || ($parm =~ /^ssh_root_pub_key/)) && ($foundkeys == 0)) {
        my ($rootkeyparm, $zonename) = split(/:/, $parm);
        my $client_zonename = xCAT::Zone->getmyzonename($client);
        my $default_zonename = xCAT::Zone->getdefaultzone();
        
        if ($zonename) {
            $parm = $rootkeyparm;           # take the zone off
            xCAT::MsgUtils->trace(0, 'I', "credentials: The node ($client) is asking for sshkeys of zone: $zonename.");
            if ($client_zonename eq $zonename) {
                my $sshbetweenodes_allowed = xCAT::Zone->enableSSHbetweennodes($client);
                if (($sshbetweenodes_allowed == 1) || ($parm =~ /^ssh_root_pub_key/)) { # check if sshbetweennodes is allowed or pub key is requested
                    $sshrootkeydir = xCAT::Zone->getzonekeydir($zonename);
                    if ($sshrootkeydir == 1) {    # error return
                        xCAT::MsgUtils->trace(0, 'W', "credentials: The zone: $zonename is not defined.");
                    } else {
                        $foundkeys = 1; # don't want to read the zone data twice
                    }
                } else {
                    xCAT::MsgUtils->trace(0, 'E', "credentials: Not allowed to read root's private ssh key because sshbetweennodes is disabled.");
                    $sshrootkeydir = 1;
                }
            } else {
                xCAT::MsgUtils->trace(0, 'E', "credentials: Not allowed to read root's private ssh key of different zone.");
                $sshrootkeydir = 1;
            }
        } elsif ($client_zonename ne $default_zonename) { # check if no zonename is submitted but node is not in default zone
            xCAT::MsgUtils->trace(0, 'E', "credentials: Not allowed to read root's private ssh key of default zone.");
            $sshrootkeydir = 1;
        }
    }

@salben21
Copy link
Author

salben21 commented Sep 9, 2022

Regarding confluent: I didn't dig in the code of confluent. Sorry.

@jjohnson42
Copy link
Member

Ok, my attempt of incorporating changes as a pull request:
#7247

Broadly speaking, at least for my part I'm working towards a broadly more hardened design in confluent for these features, and if there is interest in discussion of those security design points, let me know. I hope it is found to be a broadly more secure approach.

@besawn besawn self-assigned this Nov 4, 2022
@besawn besawn added this to the 2.16.5 milestone Nov 4, 2022
@gurevichmark gurevichmark modified the milestones: 2.16.5, 2.16.6 Mar 3, 2023
@besawn besawn modified the milestones: 2.16.6, 2.16.5 Mar 7, 2023
@besawn
Copy link
Member

besawn commented Mar 7, 2023

This issue is fixed in xCAT 2.16.5.

@besawn besawn closed this as completed Mar 7, 2023
@besawn besawn linked a pull request Mar 7, 2023 that will close this issue
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants