Skip to content

Commit

Permalink
Allow to set a public key for the root user (#173)
Browse files Browse the repository at this point in the history
* Add a widget to select a public key during installation
* Add an SSHPublicKey class
* Replace InstRootFirst with CWM widgets
* Extend PasswordWidget with an allow_empty setting
* Bump version and update changes file
* Disable root's password authentication when no password is provided
  • Loading branch information
imobachgs committed Nov 5, 2018
1 parent 6e19bcb commit 1e6fb94
Show file tree
Hide file tree
Showing 20 changed files with 1,275 additions and 55 deletions.
7 changes: 7 additions & 0 deletions package/yast2-users.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Fri Nov 2 08:46:35 UTC 2018 - igonzalezsosa@suse.com

- Allow the root user to use a public key for authentication
(fate#324690).
- 4.0.8

-------------------------------------------------------------------
Fri Oct 19 14:03:08 UTC 2018 - snwint@suse.com

Expand Down
7 changes: 6 additions & 1 deletion package/yast2-users.spec
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


Name: yast2-users
Version: 4.0.7
Version: 4.0.8
Release: 0

BuildRoot: %{_tmppath}/%{name}-%{version}-build
Expand All @@ -38,6 +38,8 @@ BuildRequires: yast2-perl-bindings
BuildRequires: yast2-security
BuildRequires: yast2-testsuite
BuildRequires: rubygem(%rb_default_ruby_abi:rspec)
# ssh-keygen
BuildRequires: openssh

Requires: cracklib
Requires: perl-Digest-SHA1
Expand All @@ -64,6 +66,9 @@ Requires: yast2-core >= 2.21.0

Requires: yast2-ruby-bindings >= 1.0.0

# ssh-keygen
Requires: openssh

Summary: YaST2 - User and Group Configuration
License: GPL-2.0
Group: System/YaST
Expand Down
7 changes: 7 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,19 @@ ylibdialog_DATA = \
lib/users/dialogs/users_to_import.rb \
lib/users/dialogs/encryption_method.rb

ywidgetdir = @ylibdir@/users/widgets
ywidget_DATA = \
lib/users/widgets/inst_root_first.rb \
lib/users/widgets/public_key_selector.rb

ylibdir = @ylibdir@/users
ylib_DATA = \
lib/users/ca_password_validator.rb \
lib/users/local_password.rb \
lib/users/encryption_method.rb \
lib/users/leaf_blk_device.rb \
lib/users/proposal.rb \
lib/users/ssh_public_key.rb \
lib/users/encryption_proposal.rb \
lib/users/ssh_authorized_keys_file.rb \
lib/users/ssh_authorized_keyring.rb \
Expand Down
71 changes: 24 additions & 47 deletions src/lib/users/dialogs/inst_root_first.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,46 @@
# current contact information at www.novell.com.
# ------------------------------------------------------------------------------

require "users/widgets"
require "users/ca_password_validator"
require "users/local_password"

require "ui/widgets"
require "cwm/dialog"
require "users/widgets/inst_root_first"

Yast.import "Mode"
Yast.import "UsersSimple"

module Yast
# This library provides a simple dialog for setting new password for the
# system adminitrator (root) including checking quality of the password
# itself. The new password is not stored here, just set in UsersSimple module
# and stored later during inst_finish.
class InstRootFirstDialog
include Yast::Logger
include Yast::I18n
include Yast::UIShortcuts

def run
Yast.import "UI"
Yast.import "Mode"
Yast.import "UsersSimple"
Yast.import "CWM"

class InstRootFirstDialog < ::CWM::Dialog
def initialize
textdomain "users"
end

return :auto unless root_password_dialog_needed?

# We do not need to create a wizard dialog in installation, but it's
# helpful when testing all manually on a running system
Wizard.CreateDialog if separate_wizard_needed?

Wizard.SetTitleIcon("yast-users")

ret = CWM.show(
content,
# Title for root-password dialogue
caption: _("Password for the System Administrator \"root\""),
)

Wizard.CloseDialog if separate_wizard_needed?

ret
# @return [String] Dialog's title
# @see CWM::AbstractWidget
def title
_("Authentication for the System Administrator \"root\"")
end

private
# @see CWM::Dialog
def run
return :auto unless root_password_dialog_needed?
super
end

# Returns a UI widget-set for the dialog
def content
VBox(
VStretch(),
HSquash(
VBox(
::Users::PasswordWidget.new(focus: true),
VSpacing(2.4),
::UI::Widgets::KeyboardLayoutTest.new
)
),
VStretch()
)
def contents
VBox(Y2Users::Widgets::InstRootFirst.new)
end

private

# Returns whether we need/ed to create new UI Wizard
def separate_wizard_needed?
#
# @note We do not need to create a wizard dialog in installation, but it's helpful when testing
# all manually on a running system
def should_open_dialog?
Mode.normal
end

Expand Down
122 changes: 122 additions & 0 deletions src/lib/users/leaf_blk_device.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# encoding: utf-8

# Copyright (c) [2018] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "json"
require "yast2/execute"

module Y2Users
# This class represents a block device reported by `lsblk` as a leaf node
#
# As we cannot use a devicegraph for the time being, this class extracts the
# block devices information from the lsblk command. Only the leaf devices
# are taken into account and it only includes information which is relevant
# for the {Y2Users::Widgets::DiskSelector} widget.
class LeafBlkDevice
class << self
# Returns all relevant block devices
#
# @note It takes the information from `lsblk`
#
# @return [Array<LeafBlkDevice>] List of relevant block devices
def all
lsblk["blockdevices"].map { |h| new_from_hash(h) }
end

# Instantiates a new object
#
# @note It uses a Hash with information from `lsblk`.
#
# @return [LeafBlkDevice] New LeafBlkDevice instance
def new_from_hash(hash)
parent = find_root_device(hash)
new(
name: hash["name"], disk: parent["name"], model: parent["model"],
transport: parent["tran"], fstype: hash["fstype"]
)
end

private

# Gets `lsblk` into a Hash
#
# @return [Hash] Hash containing data from `lsblk`
def lsblk
output = Yast::Execute.locally(
"/usr/bin/lsblk", "--inverse", "--json", "--paths",
"--output", "NAME,TRAN,FSTYPE,MODEL", stdout: :capture
)
return { "blockdevices" => [] } if output.nil?
JSON.parse(output)
end

# Finds the root for a given device
#
# @return [Hash]
def find_root_device(hash)
hash.key?("children") ? find_root_device(hash["children"][0]) : hash
end
end

# @return [String] Kernel name
attr_reader :name

# @return [String] Hardware model
attr_reader :model

# @return [String] Disk's kernel name
attr_reader :disk

# @return [Symbol] Disk's transport (:usb, :ata, etc.)
attr_reader :transport

# @return [Symbol] Filesystem type
attr_reader :fstype

# Constructor
#
# @param name [String] Kernel name
# @param disk [String] Disk's kernel name
# @param model [String] Hardware model
# @param transport [symbol] Transport
# @param fstype [Symbol] Filesystem type
def initialize(name:, disk:, model:, transport: nil, fstype: nil)
@name = name
@model = model
@disk = disk
@transport = transport.to_sym if transport
@fstype = fstype.to_sym if fstype
end

# Determines whether the device has a filesystem
#
# @return [Boolean]
def filesystem?
!!fstype
end

# Determines whether the device has a transport
#
# @return [Boolean]
def transport?
!!transport
end
end
end
76 changes: 76 additions & 0 deletions src/lib/users/ssh_public_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# encoding: utf-8

# Copyright (c) [2018] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "yast2/execute"

module Y2Users
# This class is a simplified representation of a OpenSSH public key.
#
# @example Read a public key
# key = Y2Users::SSHPublicKey.new(File.read("id_rsa.pub"))
# key.fingerprint # => "SHA256:uadPyDQj9VlFZVjK8UNp57jOnWwzGgKQJpeJEhZyV0I"
class SSHPublicKey
# Not a valid SSH public key
class InvalidKey < StandardError; end

# @return [String] Key fingerprint
attr_reader :fingerprint

# Constructor
#
# @param raw [String] Public key content
#
# @raise InvalidKey
def initialize(raw)
@fingerprint = fingerprint_from(raw)
@raw = raw.strip
end

# Returns the key comment
#
# @return [String] Comment field
def comment
@comment ||= @raw.split(" ")[2]
end

# Returns the string version of the public key
#
# @return [String]
def to_s
@raw
end

private

# Gets the fingerprint for the given OpenSSH public key
#
# @return [String] Key fingerprint
# @raise InvalidKey
def fingerprint_from(raw)
output = Yast::Execute.locally!(
["echo", raw], ["ssh-keygen", "-l", "-f", "/dev/stdin"], stdout: :capture
)
output.split(" ")[1].to_s
rescue Cheetah::ExecutionFailed
raise InvalidKey
end
end
end
Loading

0 comments on commit 1e6fb94

Please sign in to comment.