Merge lp:~larryprice/libertine/libertine-shell-ssh into lp:libertine

Proposed by Larry Price
Status: Merged
Approved by: Christopher Townsend
Approved revision: 423
Merged at revision: 459
Proposed branch: lp:~larryprice/libertine/libertine-shell-ssh
Merge into: lp:libertine
Diff against target: 373 lines (+337/-3)
5 files modified
debian/libertine-tools.install (+3/-0)
tools/CMakeLists.txt (+3/-3)
tools/completions/libertine-shell (+50/-0)
tools/libertine-shell (+255/-0)
tools/libertine-shell.1 (+26/-0)
To merge this branch: bzr merge lp:~larryprice/libertine/libertine-shell-ssh
Reviewer Review Type Date Requested Status
Christopher Townsend Approve
Libertine CI Bot continuous-integration Approve
Review via email: mp+319369@code.launchpad.net

Commit message

Add tool to easily ssh into libertine containers.

Description of the change

Add tool to easily ssh into libertine containers.

To post a comment you must log in.
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
417. By Larry Price

how did i forget to add libertine-shell to install...

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:417
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/453/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/838
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/691
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/691
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/691
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/691
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/848
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/840
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/840/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/840
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/840/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/840
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/840/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/840
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/840/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/453/rebuild

review: Approve (continuous-integration)
418. By Larry Price

prevent non-lx* containers from using libertine-shell

419. By Larry Price

merge

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:419
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/457/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/843
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/694
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/694
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/694
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/694
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/853
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/845
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/845/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/845
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/845/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/845
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/845/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/845
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/845/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/457/rebuild

review: Approve (continuous-integration)
420. By Larry Price

merge

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:420
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/509/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/915
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/751
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/751
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/751
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/751
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/926
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/915
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/915/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/915
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/915/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/915
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/915/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/915
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/915/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/509/rebuild

review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

I built the packages, installed them, and rebooted the system. I then logged in to U8, and this is what I get when running libertine-shell:

$ libertine-shell -i test-lxd
Traceback (most recent call last):
  File "/usr/bin/libertine-shell", line 248, in <module>
    main()
  File "/usr/bin/libertine-shell", line 216, in main
    with open(os.path.join(utils.get_libertine_container_home_dir(config.container_id), "libertine-shell-found-ip")) as f:
FileNotFoundError: [Errno 2] No such file or directory: '/home/townsend/.local/share/libertine-container/user-data/test-lxd/libertine-shell-found-ip'

review: Needs Fixing
421. By Larry Price

use bind mounted dir if necessary

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:421
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/510/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/916
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/752
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/752
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/752
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/752
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/927
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/916
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/916/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/916
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/916/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/916
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/916/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/916
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/916/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/510/rebuild

review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

Ok, next issue:)

I'm trying this on a machine that I have never used ssh on. I get the following error:

$ libertine-shell -i test-lxd
Traceback (most recent call last):
  File "/usr/bin/libertine-shell", line 254, in <module>
    main()
  File "/usr/bin/libertine-shell", line 229, in main
    config.setup_public_keys(hostname)
  File "/usr/bin/libertine-shell", line 83, in setup_public_keys
    all_lines, host_line = self._ssh_config_lines(hostname)
  File "/usr/bin/libertine-shell", line 148, in _ssh_config_lines
    with open(config_path, 'r') as f:
FileNotFoundError: [Errno 2] No such file or directory: '/home/townsend/.ssh/config'

review: Needs Fixing
422. By Larry Price

don't read non-existent config

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:422
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/512/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/921
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/757
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/757
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/757
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/757
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/932
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/921
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/921/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/921
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/921/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/921
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/921/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/921
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/921/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/512/rebuild

review: Approve (continuous-integration)
423. By Larry Price

specify container type in help/manpage

Revision history for this message
Christopher Townsend (townsend) wrote :

Ok, works fine. +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/libertine-tools.install'
--- debian/libertine-tools.install 2017-03-13 15:50:54 +0000
+++ debian/libertine-tools.install 2017-04-07 13:51:54 +0000
@@ -1,3 +1,6 @@
1usr/bin/libertine-container-manager1usr/bin/libertine-container-manager
2usr/bin/libertine-shell
2usr/share/bash-completion/completions/libertine-container-manager3usr/share/bash-completion/completions/libertine-container-manager
3usr/share/man/*/libertine-container-manager.14usr/share/man/*/libertine-container-manager.1
5usr/share/bash-completion/completions/libertine-shell
6usr/share/man/*/libertine-shell.1
47
=== modified file 'tools/CMakeLists.txt'
--- tools/CMakeLists.txt 2017-01-31 16:28:31 +0000
+++ tools/CMakeLists.txt 2017-04-07 13:51:54 +0000
@@ -1,9 +1,9 @@
1install(PROGRAMS libertine-container-manager libertine-launch1install(PROGRAMS libertine-container-manager libertine-launch libertine-shell
2 libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined2 libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined
3 DESTINATION ${CMAKE_INSTALL_BINDIR})3 DESTINATION ${CMAKE_INSTALL_BINDIR})
4install(FILES libertine-launch.1 libertine-container-manager.14install(FILES libertine-launch.1 libertine-container-manager.1
5 libertine-xmir.15 libertine-xmir.1 libertine-shell.1
6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man16 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
7 COMPONENT doc)7 COMPONENT doc)
8install(FILES completions/libertine-container-manager8install(FILES completions/libertine-container-manager completions/libertine-shell
9 DESTINATION ${DESTDIR}/usr/share/bash-completion/completions/)9 DESTINATION ${DESTDIR}/usr/share/bash-completion/completions/)
1010
=== added file 'tools/completions/libertine-shell'
--- tools/completions/libertine-shell 1970-01-01 00:00:00 +0000
+++ tools/completions/libertine-shell 2017-04-07 13:51:54 +0000
@@ -0,0 +1,50 @@
1# Copyright (C) 2017 Canonical Ltd.
2#
3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; version 3 of the License.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.
14
15
16_libertine-shell()
17{
18 local cur cmd opts
19 COMPREPLY=()
20 cur=${COMP_WORDS[COMP_CWORD]}
21
22 if [[ ${COMP_CWORD} -gt 1 ]]; then
23 cmd="$cur"
24 if [[ ${cmd} != -* ]]; then
25 cmd=${COMP_WORDS[COMP_CWORD-1]}
26 fi
27
28 case "${cmd}" in
29 "--username" )
30 opts=$(users)
31 ;;
32 "--identity-file" )
33 compopt -o filenames
34 local files=("${cur}"*)
35 [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]// /\ }" )
36 return 0
37 ;;
38 esac
39 fi
40
41 if [[ -z ${opts} ]]; then
42 opts="--help --identity-file --username --assume-yes --id"
43 fi
44
45 if [[ -n "${opts}" ]]; then
46 COMPREPLY=( $(compgen -W "${opts}" -- "${COMP_WORDS[COMP_CWORD]}") )
47 return 0
48 fi
49}
50complete -F _libertine-shell libertine-shell
051
=== added file 'tools/libertine-shell'
--- tools/libertine-shell 1970-01-01 00:00:00 +0000
+++ tools/libertine-shell 2017-04-07 13:51:54 +0000
@@ -0,0 +1,255 @@
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2017 Canonical Ltd.
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; version 3 of the License.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18import argparse
19import os
20import shlex
21import subprocess
22import sys
23
24from libertine import ContainersConfig, utils, Libertine
25from shutil import copyfile
26
27import gettext
28gettext.textdomain('libertine')
29_ = gettext.gettext
30
31class ShellConfig(object):
32 def __init__(self, args):
33 super().__init__()
34 self._validate(self._parse(args))
35
36 @property
37 def public_key(self):
38 return "{}.pub".format(self.identity_file)
39
40 def _parse(self, args):
41 arg_parser = argparse.ArgumentParser(description=_('Launch an SSH session within a lxc/lxd Libertine container'))
42 arg_parser.add_argument('-i', '--id',
43 help=_('Container identifier'))
44 arg_parser.add_argument('-u', '--username',
45 help=_('Container username'))
46 arg_parser.add_argument('-f', '--identity-file',
47 help=_('SSH key to be used'))
48 arg_parser.add_argument('-y', '--assume-yes',
49 action='store_true',
50 help=_('Assume yes to all prompts'))
51
52 return arg_parser.parse_args(args=args)
53
54 def _validate(self, options):
55 self.assume_yes = options.assume_yes or False
56 self.username = options.username
57
58 config = ContainersConfig.ContainersConfig()
59 self.container_id = config.check_container_id(options.id)
60 self.container_type = config.get_container_type(self.container_id)
61 utils.get_logger().debug("Using Container ID: {}".format(self.container_id))
62
63 self.identity_file = None
64
65 if options.identity_file:
66 utils.get_logger().debug("Public key file path: {}".format(options.identity_file))
67
68 if not os.path.exists(options.identity_file):
69 if not os.path.dirname(options.identity_file) and os.path.exists(os.path.join(os.environ['HOME'], '.ssh', options.identity_file)):
70 self.identity_file = os.path.join(os.environ['HOME'], '.ssh', options.identity_file)
71 else:
72 utils.get_logger().error(_("Identity file not found at '{}'. Leave blank for default.").format(options.identity_file))
73 sys.exit(1)
74 else:
75 self.identity_file = options.identity_file
76
77 if not os.path.exists(self.public_key):
78 utils.get_logger().error(_("Corresponding public key not found for '{}'.").format(self.identity_file))
79 sys.exit(1)
80
81 def setup_public_keys(self, hostname):
82 should_save = False
83 all_lines, host_line = self._ssh_config_lines(hostname)
84
85 if host_line:
86 identity_files = [line for line in host_line.split('\n') if "IdentityFile " in line]
87 if identity_files:
88 if not self.identity_file:
89 self.identity_file = identity_files[-1].split(' ')[-1]
90 elif not [i for i in identity_files if self.identity_file in i]:
91 should_save = True
92 host_line += "\n\tIdentityFile {}".format(self.identity_file)
93 if self.username and not "User {}".format(self.username) in host_line:
94 host_line += "\tUser {}\n".format(self.username)
95 elif self.identity_file:
96 should_save = True
97 host_line = "Host {}\n\tIdentityFile {}".format(hostname, self.identity_file)
98 all_lines.append(host_line)
99
100 if host_line and self.username and not "User {}".format(self.username) in host_line:
101 should_save = True
102 host_line += "\n\tUser {}\n".format(self.username)
103
104 if not self.identity_file:
105 DEFAULT_KEYS = ["id_dsa", "id_rsa", "id_ecdsa", "id_ed25519"]
106 keys = [key for key in os.listdir(os.path.join(os.environ['HOME'], '.ssh')) if key in DEFAULT_KEYS]
107 if keys:
108 self.identity_file = os.path.join(os.environ['HOME'], '.ssh', keys[0])
109
110 if self.identity_file and not (os.path.exists(self.identity_file) or os.path.exists(self.public_key)):
111 utils.get_logger().error(_("Configured identity file or public key matching '{}' do not exist.").format(self.identity_file))
112 sys.exit(1)
113
114 if should_save and self._ask_save_ssh_config():
115 try:
116 config_path = os.path.join(os.environ['HOME'], ".ssh", "config")
117 copyfile(config_path, config_path + ".bak")
118 with open(config_path, 'w') as f:
119 for i in range(0, len(all_lines)):
120 if i == (len(all_lines)-1):
121 newlines = '\n'
122 else:
123 newlines = '\n\n'
124
125 if not all_lines[i].startswith("Host {}".format(hostname)):
126 f.write(all_lines[i] + newlines)
127 else:
128 f.write(host_line + newlines)
129 except Exception as e:
130 utils.get_logger().warning("Error caught during config edit: {}.".format(str(e)))
131 utils.get_logger().warning("Restoring previous version of '{}' and continuing".format(config_path))
132 copyfile(config_path + ".bak", config_path)
133
134 os.remove(config_path + ".bak")
135
136 def _ssh_config_lines(self, hostname):
137 ssh_dir = os.path.join(os.environ['HOME'], ".ssh")
138 if not os.path.exists(ssh_dir):
139 utils.get_logger().error(_("It looks like no SSH keys are set up. Please generate a key and try again. "
140 "You can use the following command to generate an appropriate key:\n"
141 "\tssh-keygen -t rsa -b 4096 -C 'your_email@example.com'"))
142 sys.exit(1)
143
144 config_path = os.path.join(ssh_dir, "config")
145
146 all_lines = []
147 host_line = []
148 if os.path.exists(config_path):
149 with open(config_path, 'r') as f:
150 all_lines = [line.strip() for line in f.read().split('\n\n')]
151 host_line = [line for line in all_lines if line.startswith("Host {}".format(hostname))]
152 if host_line:
153 host_line = host_line[-1]
154
155 return (all_lines, host_line or '')
156
157 def _ask_save_ssh_config(self):
158 if not self.assume_yes:
159 if self.username:
160 prompt = input(_("Always use '{}' as identity file and username '{}' "
161 "when connecting to '{}'? [Yn]").format(self.identity_file, self.username, self.container_id))
162 else:
163 prompt = input(_("Always use '{}' as identity file when connecting to '{}'? [Yn]").format(self.identity_file, self.container_id))
164
165 if not (prompt == _('Y') or prompt == _('y') or prompt == ''):
166 utils.get_logger().debug("Responded 'no' to saving identity configuration")
167 return False
168
169 return True
170
171 def command(self, hostname):
172 if self.username:
173 hostname = "{}@{}".format(self.username, hostname)
174
175 options = ""
176 if self.identity_file:
177 options += " -i {} ".format(self.identity_file)
178
179 return "ssh {} {} -t 'bash -s'".format(options, hostname)
180
181 def get_ip_host_file_path(self, home):
182 if home in ContainersConfig.ContainersConfig().get_container_bind_mounts(self.container_id):
183 return os.path.join(home, 'libertine-shell-found-ip')
184
185 return os.path.join(utils.get_libertine_container_home_dir(self.container_id), 'libertine-shell-found-ip')
186
187
188def main():
189 if subprocess.Popen(shlex.split("bash -c \"which sshd &> /dev/null\"")).wait() != 0:
190 utils.get_logger().error(_("No sshd found. You can install openssh with the following command:\n"
191 "\tapt install openssh-client"))
192 sys.exit(1)
193
194 config = ShellConfig(sys.argv[1:])
195
196 if not (config.container_type == 'lxd' or config.container_type == 'lxc'):
197 utils.get_logger().error(_("'{}' is a '{}' container. Only 'lxd' or 'lxc' "
198 "containers are able to use this tool.".format(
199 config.container_id, config.container_type)))
200 sys.exit(1)
201
202 container = Libertine.LibertineContainer(config.container_id)
203
204
205 with Libertine.ContainerRunning(container.container):
206 if not container.container._binary_exists("sshd"):
207 if not config.assume_yes:
208 prompt = input(_("openssh-server not detected in container '{}'. Install now? [Yn]").format(config.container_id))
209 if not (prompt == _('Y') or prompt == _('y') or prompt == ''):
210 utils.get_logger().debug("Responded 'no' to openssh-server installation")
211 sys.exit(1)
212
213 if not container.install_package("openssh-server") or not container.exec_command("update-rc.d ssh defaults"):
214 utils.get_logger().error(_("Failed to install openssh-server"))
215 sys.exit(1)
216
217 username = config.username or os.environ['USER']
218
219 if not container.exec_command('bash -c "hostname -I | awk \'{ print $1 }\' > /home/%s/libertine-shell-found-ip"' % username):
220 utils.get_logger().error(_("Unable to get IP address for '{}'".format(config.container_id)))
221 sys.exit(1)
222
223 with open(config.get_ip_host_file_path('/home/%s' % username)) as f:
224 hostname = f.read().strip()
225 utils.get_logger().debug(hostname)
226 if not hostname:
227 utils.get_logger().error(_("Unable to get IP address for '{}'".format(config.container_id)))
228 sys.exit(1)
229
230 config.setup_public_keys(hostname)
231
232 if not container.exec_command('test -e /home/{}/.ssh/authorized_keys'.format(username)):
233 container.exec_command("bash -c 'mkdir -p /home/{user}/.ssh && "
234 "chown {user}:{user} /home/{user}/.ssh'".format(user=username))
235 container.exec_command("bash -c 'touch /home/{user}/.ssh/authorized_keys && "
236 "chown {user}:{user} /home/{user}/.ssh/authorized_keys'".format(user=username))
237
238 with open(config.public_key, 'r') as f:
239 public_key = f.read().strip()
240 if not container.exec_command('grep -q "{}$" /home/{}/.ssh/authorized_keys'.format(public_key, username)):
241 if not config.assume_yes:
242 prompt = input(_("OK to add public key '{}' to container '{}'? [Yn]").format(config.public_key, config.container_id))
243 if not (prompt == _('Y') or prompt == _('y') or prompt == ''):
244 utils.get_logger().error(_("Public key must be added to container to continue."))
245 sys.exit(1)
246
247 if not container.exec_command('bash -c "echo {} >> /home/{}/.ssh/authorized_keys"'.format(public_key, username)):
248 utils.get_logger().error(_("Failed to add public key to container's authorized keys."))
249 sys.exit(1)
250
251 subprocess.call(shlex.split(config.command(hostname)))
252
253
254if __name__ == '__main__':
255 main()
0256
=== added file 'tools/libertine-shell.1'
--- tools/libertine-shell.1 1970-01-01 00:00:00 +0000
+++ tools/libertine-shell.1 2017-04-07 13:51:54 +0000
@@ -0,0 +1,26 @@
1.TH libertine-shell "1" "March 2017" "libertine-shell 1.0" "User Commands"
2
3.SH NAME
4libertine-shell \- Launch an SSH session connected to a lxc/lxd Libertine container
5
6.SH DESCRIPTION
7usage: libertine\-shell [\-h] [-i ID] [-u USER] [-f IDENTITY_FILE_PATH] [-y]
8.PP
9Launch an SSH session connected to a Libertine container
10
11.SS "optional arguments:"
12.TP
13\fB\-h\fR, \fB\-\-help\fR
14show this help message and exit
15.TP
16\fB\-i\fR, \fB\-\-id\fR
17Container identifier (default container if none specified)
18.TP
19\fB\-u\fR, \fB\-\-username\fR
20Container username (current username if none specified)
21.TP
22\fB\-f\fR, \fB\-\-identity-file\fR
23Full path of SSH key to be used
24.TP
25\fB\-y\fR, \fB\-\-assume-yes\fR
26Assume yes to all prompts (installations, public key copy, configuration changes)

Subscribers

People subscribed via source and target branches