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

Subscribers

People subscribed via source and target branches