Merge pull request #202 from CanonicalLtd/mwhudson/ubuntu-core-login-screen
rewrite login screen per feedback
This commit is contained in:
commit
2944098c98
|
@ -13,12 +13,14 @@ stty icrnl -echo
|
||||||
if [ "$(snap managed)" = "true" ]; then
|
if [ "$(snap managed)" = "true" ]; then
|
||||||
# check if we have extrausers that have no password set
|
# check if we have extrausers that have no password set
|
||||||
if grep -qE '^[-a-z0-9+.-_]+:x:' /var/lib/extrausers/passwd && ! grep -qE '^[-a-z0-9+.-_]+:\$[0-9]+\$.*:' /var/lib/extrausers/shadow; then
|
if grep -qE '^[-a-z0-9+.-_]+:x:' /var/lib/extrausers/passwd && ! grep -qE '^[-a-z0-9+.-_]+:\$[0-9]+\$.*:' /var/lib/extrausers/shadow; then
|
||||||
if [ ! -f /run/console-conf/login-details.txt ]; then
|
tty=$(tty)
|
||||||
|
tty=$(echo ${tty#/dev/} | tr '/' '-')
|
||||||
|
if [ ! -f /run/console-conf/login-details-${tty}.txt ]; then
|
||||||
mkdir -p /run/console-conf
|
mkdir -p /run/console-conf
|
||||||
/usr/share/subiquity/console-conf-write-login-details > /run/console-conf/login-details.txt.tmp
|
/usr/share/subiquity/console-conf-write-login-details > /run/console-conf/login-details-${tty}.txt.tmp
|
||||||
mv /run/console-conf/login-details.txt.tmp /run/console-conf/login-details.txt
|
mv /run/console-conf/login-details-${tty}.txt.tmp /run/console-conf/login-details-${tty}.txt
|
||||||
fi
|
fi
|
||||||
cat /run/console-conf/login-details.txt
|
cat /run/console-conf/login-details-${tty}.txt
|
||||||
read REPLY
|
read REPLY
|
||||||
else
|
else
|
||||||
touch /var/lib/console-conf/complete
|
touch /var/lib/console-conf/complete
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from subiquitycore.controller import BaseController
|
from subiquitycore.controller import BaseController
|
||||||
|
@ -47,36 +46,78 @@ def get_device_owner():
|
||||||
return result
|
return result
|
||||||
return None
|
return None
|
||||||
|
|
||||||
login_details_tmpl = """\
|
def host_key_fingerprints():
|
||||||
Congratulations! This device is now registered to {realname}.
|
"""Query sshd to find the host keys and then fingerprint them.
|
||||||
|
|
||||||
The next step is to log into the device via ssh:
|
Returns a sequence of (key-type, fingerprint) pairs.
|
||||||
|
"""
|
||||||
|
config = run_command(['sshd', '-T'])
|
||||||
|
if config['status'] != 0:
|
||||||
|
log.debug("sshd -T failed %r", config['err'])
|
||||||
|
return []
|
||||||
|
keyfiles = []
|
||||||
|
for line in config['output'].splitlines():
|
||||||
|
if line.startswith('hostkey '):
|
||||||
|
keyfiles.append(line.split(None, 1)[1])
|
||||||
|
info = []
|
||||||
|
for keyfile in keyfiles:
|
||||||
|
result = run_command(['ssh-keygen', '-lf', keyfile])
|
||||||
|
if result['status'] != 0:
|
||||||
|
log.debug("ssh-keygen -lf %s failed %r", keyfile, result['err'])
|
||||||
|
continue
|
||||||
|
parts = result['output'].strip().split()
|
||||||
|
length, fingerprint, host, keytype = parts
|
||||||
|
keytype = keytype.strip('()')
|
||||||
|
info.append((keytype, fingerprint))
|
||||||
|
return info
|
||||||
|
|
||||||
{sshcommands}
|
|
||||||
These keys can be used to log in:
|
|
||||||
|
|
||||||
{sshkeys}
|
host_keys_intro = """
|
||||||
Once you've logged in, you can optionally set a password by running
|
The host key fingerprints are:
|
||||||
"sudo passwd $USER". After you've set a password, you can also use it
|
|
||||||
to log in here.
|
|
||||||
|
|
||||||
You can explore snappy core with snap --help.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def write_login_details(fp, realname, username, ips, fingerprints):
|
host_key_tmpl = """\
|
||||||
sshcommands = ""
|
{keytype:{width}} {fingerprint}
|
||||||
|
"""
|
||||||
|
|
||||||
|
single_host_key_tmpl = """\
|
||||||
|
The {keytype} host key fingerprints is:
|
||||||
|
{fingerprint}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def host_key_info():
|
||||||
|
fingerprints = host_key_fingerprints()
|
||||||
|
if len(fingerprints) == 1:
|
||||||
|
[(keytype, fingerprint)] = fingerprints
|
||||||
|
return single_host_key_tmpl.format(keytype=keytype, fingerprint=fingerprint)
|
||||||
|
lines = [host_keys_intro]
|
||||||
|
longest_type = max([len(keytype) for keytype, _ in fingerprints])
|
||||||
|
for keytype, fingerprint in fingerprints:
|
||||||
|
lines.append(host_key_tmpl.format(keytype=keytype, fingerprint=fingerprint, width=longest_type))
|
||||||
|
return "".join(lines)
|
||||||
|
|
||||||
|
login_details_tmpl = """\
|
||||||
|
Ubuntu Core 16 on {first_ip} ({tty_name})
|
||||||
|
{host_key_info}
|
||||||
|
To login:
|
||||||
|
{sshcommands}
|
||||||
|
Personalize your account at https://login.ubuntu.com.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def write_login_details(fp, username, ips):
|
||||||
|
sshcommands = "\n"
|
||||||
for ip in ips:
|
for ip in ips:
|
||||||
sshcommands += " ssh %s@%s\n"%(username, ip)
|
sshcommands += " ssh %s@%s\n"%(username, ip)
|
||||||
sshkeys = ""
|
tty_name = os.ttyname(0)[5:] # strip off the /dev/
|
||||||
for fingerprint in fingerprints:
|
fp.write(login_details_tmpl.format(
|
||||||
sshkeys += " " + fingerprint + "\n"
|
sshcommands=sshcommands, host_key_info=host_key_info(), tty_name=tty_name, first_ip=ips[0]))
|
||||||
fp.write(login_details_tmpl.format(realname=realname, username=username, sshcommands=sshcommands, sshkeys=sshkeys))
|
|
||||||
|
|
||||||
def write_login_details_standalone():
|
def write_login_details_standalone():
|
||||||
owner = get_device_owner()
|
owner = get_device_owner()
|
||||||
if owner is None:
|
if owner is None:
|
||||||
# Nothing much we can do :/
|
print("No device owner details found.")
|
||||||
print("No device owner details found")
|
|
||||||
return 0
|
return 0
|
||||||
from probert import network
|
from probert import network
|
||||||
from subiquitycore.models.network import NETDEV_IGNORED_IFACE_NAMES, NETDEV_IGNORED_IFACE_TYPES
|
from subiquitycore.models.network import NETDEV_IGNORED_IFACE_NAMES, NETDEV_IGNORED_IFACE_TYPES
|
||||||
|
@ -90,10 +131,9 @@ def write_login_details_standalone():
|
||||||
if l.name in NETDEV_IGNORED_IFACE_NAMES:
|
if l.name in NETDEV_IGNORED_IFACE_NAMES:
|
||||||
continue
|
continue
|
||||||
for _, addr in sorted(l.addresses.items()):
|
for _, addr in sorted(l.addresses.items()):
|
||||||
|
if addr.scope == "global":
|
||||||
ips.append(addr.ip)
|
ips.append(addr.ip)
|
||||||
key_file = os.path.join(owner['homedir'], ".ssh/authorized_keys")
|
write_login_details(sys.stdout, owner['username'], ips)
|
||||||
fingerprints = run_command(['ssh-keygen', '-lf', key_file])['output'].replace('\r', '').splitlines()
|
|
||||||
write_login_details(sys.stdout, owner['realname'], owner['username'], ips, fingerprints)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,7 +164,6 @@ class IdentityController(BaseController):
|
||||||
'username': email,
|
'username': email,
|
||||||
}
|
}
|
||||||
self.model.add_user(result)
|
self.model.add_user(result)
|
||||||
ssh_keys = subprocess.getoutput('ssh-add -L').splitlines()
|
|
||||||
login_details_path = '.subiquity/login-details.txt'
|
login_details_path = '.subiquity/login-details.txt'
|
||||||
else:
|
else:
|
||||||
self.ui.frame.body.progress.set_text("Contacting store...")
|
self.ui.frame.body.progress.set_text("Contacting store...")
|
||||||
|
@ -140,24 +179,15 @@ class IdentityController(BaseController):
|
||||||
'realname': email,
|
'realname': email,
|
||||||
'username': data['username'],
|
'username': data['username'],
|
||||||
}
|
}
|
||||||
ssh_keys = data['ssh-keys']
|
|
||||||
os.makedirs('/run/console-conf', exist_ok=True)
|
os.makedirs('/run/console-conf', exist_ok=True)
|
||||||
login_details_path = '/run/console-conf/login-details.txt'
|
login_details_path = '/run/console-conf/login-details.txt'
|
||||||
self.model.add_user(result)
|
self.model.add_user(result)
|
||||||
log.debug('ssh_keys %s', ssh_keys)
|
|
||||||
fingerprints = []
|
|
||||||
for key in ssh_keys:
|
|
||||||
keygen_result = subprocess.Popen(['ssh-keygen', '-lf', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
||||||
fingerprint, err = keygen_result.communicate(key.encode('utf-8'))
|
|
||||||
fingerprints.append(fingerprint.decode('utf-8', 'replace').replace('\r', '').strip())
|
|
||||||
self.model.user.fingerprints = fingerprints
|
|
||||||
log.debug('fingerprints %s', fingerprints)
|
|
||||||
ips = []
|
ips = []
|
||||||
net_model = self.controllers['Network'].model
|
net_model = self.controllers['Network'].model
|
||||||
for dev in net_model.get_all_netdevs():
|
for dev in net_model.get_all_netdevs():
|
||||||
ips.extend(dev.actual_ip_addresses)
|
ips.extend(dev.actual_global_ip_addresses)
|
||||||
with open(login_details_path, 'w') as fp:
|
with open(login_details_path, 'w') as fp:
|
||||||
write_login_details(fp, result['realname'], result['username'], ips, fingerprints)
|
write_login_details(fp, result['username'], ips)
|
||||||
self.login()
|
self.login()
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
|
|
|
@ -70,7 +70,7 @@ class LoginView(BaseView):
|
||||||
login_text += remote_tpl.format(**login_info)
|
login_text += remote_tpl.format(**login_info)
|
||||||
ips = []
|
ips = []
|
||||||
for dev in self.netdevs:
|
for dev in self.netdevs:
|
||||||
for addr in dev.actual_ip_addresses:
|
for addr in dev.actual_global_ip_addresses:
|
||||||
ips.append(addr)
|
ips.append(addr)
|
||||||
|
|
||||||
sl += [Text(login_text), Padding.line_break("")]
|
sl += [Text(login_text), Padding.line_break("")]
|
||||||
|
@ -78,15 +78,6 @@ class LoginView(BaseView):
|
||||||
ssh_iface = " ssh %s@%s" % (user.username, ip)
|
ssh_iface = " ssh %s@%s" % (user.username, ip)
|
||||||
sl.append(Text(ssh_iface))
|
sl.append(Text(ssh_iface))
|
||||||
|
|
||||||
sl += [
|
|
||||||
Padding.line_break(""),
|
|
||||||
Text("SSH keys with the following fingerprints can be used to log in:"),
|
|
||||||
Padding.line_break(""),
|
|
||||||
]
|
|
||||||
|
|
||||||
for fingerprint in user.fingerprints:
|
|
||||||
sl.append(Text(" " + fingerprint))
|
|
||||||
|
|
||||||
return Pile(sl)
|
return Pile(sl)
|
||||||
|
|
||||||
def confirm(self, result):
|
def confirm(self, result):
|
||||||
|
|
|
@ -231,6 +231,10 @@ class Networkdev:
|
||||||
r.append(ip)
|
r.append(ip)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@property
|
||||||
|
def actual_global_ip_addresses(self):
|
||||||
|
return [addr.ip for _, addr in sorted(self._net_info.addresses.items()) if addr.scope == "global"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def configured_ip_addresses(self):
|
def configured_ip_addresses(self):
|
||||||
return self._configuration.setdefault('addresses', [])
|
return self._configuration.setdefault('addresses', [])
|
||||||
|
|
Loading…
Reference in New Issue