bectl(8): bectl jail improvements

- Support passing arbitrary jail arguments via -o
- Split the related (and rewritten since the GSoC) jail bits out into a new
  bectl_jail.c file, to reduce clutter in bectl.c
- Don't use RFC 1918 IP space [0]; we'll instead set no default IPv4 and let
  the user pass in any address options they wish via -o

Reported by:	rgrimes [0], Shawn Webb [0]
This commit is contained in:
Kyle Evans 2018-08-06 03:32:25 +00:00
parent 9a460e6fd2
commit d694059f1e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/bectl/; revision=337368
5 changed files with 378 additions and 161 deletions

View file

@ -3,6 +3,8 @@
PROG= bectl
MAN= bectl.8
SRCS= bectl.c bectl_jail.c
LIBADD+= be
LIBADD+= jail
LIBADD+= nvpair

View file

@ -14,7 +14,7 @@
.\" @(#)be.1
.\" $FreeBSD$
.\"
.Dd July 24, 2018
.Dd August 5, 2018
.Dt BECTL 8
.Os FreeBSD
.Sh NAME
@ -40,6 +40,7 @@ destroy
.Ao Ar beName | beName@snapshot Ac
.Nm
jail
.Op Fl o Ar key Ns = Ns Ar value Oc Ns ...
.Ao Ar jailID | jailName Ac
.Ao Ar bootenv Ac
.Nm
@ -123,10 +124,32 @@ Specifying
will automatically unmount without confirmation.
.Pp
.It Ic jail
.Op Fl o Ar key Ns = Ns Ar value Oc Ns ...
.Ao Ar jailID | jailName Ac
.Ao Ar bootenv Ac
.Pp
Creates a jail of the given boot environment.
Multiple
.Fl o
arguments may be specified.
Al
.Ar key ,
.Ar value
pairs are interpreted as jail parameters as described in
.Xr jail 8 .
The following default parameters are provided:
.Bl -tag -width -indent
.It Va allow.mount Ns = Ns Ar true
.It Va allow.mount.devfs Ns = Ns Ar true
.It Va enforce_statfs Ns = Ns Ar 1
.It Va name Ns = Ns Ar bootenv
.It Va host.hostname Ns = Ns Ar bootenv
.It Va path
Set to a path in /tmp generated by
.Xr libbe 8 .
.El
.pp
All default parameters may be overwritten.
.Pp
.It Ic list
.Op Fl a

View file

@ -27,10 +27,8 @@
*/
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/mount.h>
#include <errno.h>
#include <jail.h>
#include <libutil.h>
#include <stdbool.h>
#include <stdio.h>
@ -43,6 +41,8 @@
#include <be.h>
#include "bectl.h"
#define HEADER_BE "BE"
#define HEADER_BEPLUS "BE/Dataset/Snapshot"
#define HEADER_ACTIVE "Active"
@ -71,7 +71,6 @@ static int bectl_cmd_destroy(int argc, char *argv[]);
static int bectl_cmd_export(int argc, char *argv[]);
static int bectl_cmd_import(int argc, char *argv[]);
static int bectl_cmd_add(int argc, char *argv[]);
static int bectl_cmd_jail(int argc, char *argv[]);
static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops);
static void print_padding(const char *fval, int colsz, struct printc *pc);
static int print_snapshots(const char *dsname, struct printc *pc);
@ -80,14 +79,11 @@ static void print_headers(nvlist_t *props, struct printc *pc);
static int bectl_cmd_list(int argc, char *argv[]);
static int bectl_cmd_mount(int argc, char *argv[]);
static int bectl_cmd_rename(int argc, char *argv[]);
static int bectl_search_jail_paths(const char *mnt);
static int bectl_locate_jail(const char *ident);
static int bectl_cmd_unjail(int argc, char *argv[]);
static int bectl_cmd_unmount(int argc, char *argv[]);
static libbe_handle_t *be;
libbe_handle_t *be;
static int
int
usage(bool explicit)
{
FILE *fp;
@ -102,7 +98,7 @@ usage(bool explicit)
"\tbectl export sourceBe\n"
"\tbectl import targetBe\n"
"\tbectl add (path)*\n"
"\tbectl jail bootenv\n"
"\tbectl jail [ -o key=value ]... bootenv\n"
"\tbectl list [-a] [-D] [-H] [-s]\n"
"\tbectl mount beName [mountpoint]\n"
"\tbectl rename origBeName newBeName\n"
@ -379,58 +375,6 @@ bectl_cmd_destroy(int argc, char *argv[])
return (err);
}
static int
bectl_cmd_jail(int argc, char *argv[])
{
char *bootenv;
char mnt_loc[BE_MAXPATHLEN];
int err, jid;
/* struct jail be_jail = { 0 }; */
if (argc == 1) {
fprintf(stderr, "bectl jail: missing boot environment name\n");
return (usage(false));
}
if (argc > 2) {
fprintf(stderr, "bectl jail: too many arguments\n");
return (usage(false));
}
bootenv = argv[1];
/*
* XXX TODO: if its already mounted, perhaps there should be a flag to
* indicate its okay to proceed??
*/
if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) {
fprintf(stderr, "could not mount bootenv\n");
return (1);
}
/* XXX TODO: Make the IP/hostname configurable? */
jid = jail_setv(JAIL_CREATE | JAIL_ATTACH,
"name", bootenv,
"path", mnt_loc,
"host.hostname", bootenv,
"persist", "true",
"ip4.addr", "10.20.30.40",
"allow.mount", "true",
"allow.mount.devfs", "true",
"enforce_statfs", "1",
NULL);
if (jid == -1) {
fprintf(stderr, "unable to create jail. error: %d\n", errno);
return (1);
}
/* We're attached within the jail... good bye! */
chdir("/");
execl("/bin/sh", "/bin/sh", NULL);
return (0);
}
/*
* Given a set of dataset properties (for a BE dataset), populate originprops
* with the origin's properties.
@ -851,105 +795,6 @@ bectl_cmd_rename(int argc, char *argv[])
return (0);
}
static int
bectl_search_jail_paths(const char *mnt)
{
char jailpath[MAXPATHLEN + 1];
int jid;
jid = 0;
(void)mnt;
while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath,
NULL)) != -1) {
if (strcmp(jailpath, mnt) == 0)
return (jid);
}
return (-1);
}
/*
* Locate a jail based on an arbitrary identifier. This may be either a name,
* a jid, or a BE name. Returns the jid or -1 on failure.
*/
static int
bectl_locate_jail(const char *ident)
{
nvlist_t *belist, *props;
char *mnt;
int jid;
/* Try the easy-match first */
jid = jail_getid(ident);
if (jid != -1)
return (jid);
/* Attempt to try it as a BE name, first */
if (be_prop_list_alloc(&belist) != 0)
return (-1);
if (be_get_bootenv_props(be, belist) != 0)
return (-1);
if (nvlist_lookup_nvlist(belist, ident, &props) == 0) {
/* We'll attempt to resolve the jid by way of mountpoint */
if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) {
jid = bectl_search_jail_paths(mnt);
be_prop_list_free(belist);
return (jid);
}
be_prop_list_free(belist);
}
return (-1);
}
static int
bectl_cmd_unjail(int argc, char *argv[])
{
char path[MAXPATHLEN + 1];
char *cmd, *name, *target;
int jid;
/* Store alias used */
cmd = argv[0];
if (argc != 2) {
fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
return (usage(false));
}
target = argv[1];
/* Locate the jail */
if ((jid = bectl_locate_jail(target)) == -1) {
fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, target);
return (1);
}
bzero(&path, MAXPATHLEN + 1);
name = jail_getname(jid);
if (jail_getv(0, "name", name, "path", path, NULL) != jid) {
free(name);
fprintf(stderr, "bectl %s: failed to get path for jail requested by '%s'\n", cmd, target);
return (1);
}
free(name);
if (be_mounted_at(be, path, NULL) != 0) {
fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", cmd, target);
return (1);
}
jail_remove(jid);
unmount(path, 0);
return (0);
}
static int
bectl_cmd_unmount(int argc, char *argv[])
{

35
sbin/bectl/bectl.h Normal file
View file

@ -0,0 +1,35 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
int usage(bool explicit);
int bectl_cmd_jail(int argc, char *argv[]);
int bectl_cmd_unjail(int argc, char *argv[]);
extern libbe_handle_t *be;

312
sbin/bectl/bectl_jail.c Normal file
View file

@ -0,0 +1,312 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/mount.h>
#include <err.h>
#include <jail.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <be.h>
#include "bectl.h"
static void jailparam_grow(void);
static void jailparam_add(const char *name, const char *val);
static bool jailparam_addarg(char *arg);
static int bectl_search_jail_paths(const char *mnt);
static int bectl_locate_jail(const char *ident);
/* We'll start with 8 parameters initially and grow as needed. */
#define INIT_PARAMCOUNT 8
static struct jailparam *jp;
static int jpcnt;
static int jpused;
static char mnt_loc[BE_MAXPATHLEN + 1];
static void
jailparam_grow(void)
{
jpcnt *= 2;
jp = realloc(jp, jpcnt * sizeof(*jp));
if (jp == NULL)
err(2, "realloc");
}
static void
jailparam_add(const char *name, const char *val)
{
int i;
for (i = 0; i < jpused; ++i) {
if (strcmp(name, jp[i].jp_name) == 0)
break;
}
if (i < jpused)
jailparam_free(&jp[i], 1);
else if (jpused == jpcnt)
/* The next slot isn't allocated yet */
jailparam_grow();
if (jailparam_init(&jp[i], name) != 0)
return;
if (jailparam_import(&jp[i], val) != 0)
return;
++jpused;
}
static bool
jailparam_addarg(char *arg)
{
char *name, *val;
if (arg == NULL)
return (false);
name = arg;
if ((val = strchr(arg, '=')) == NULL) {
fprintf(stderr, "bectl jail: malformed jail option '%s'\n",
arg);
return (false);
}
*val++ = '\0';
if (strcmp(name, "path") == 0) {
if (strlen(val) > BE_MAXPATHLEN) {
fprintf(stderr,
"bectl jail: skipping too long path assignment '%s' (max length = %d)\n",
val, BE_MAXPATHLEN);
return (false);
}
strcpy(mnt_loc, val);
}
jailparam_add(name, val);
return (true);
}
int
bectl_cmd_jail(int argc, char *argv[])
{
char *bootenv, *mountpoint;
int jid, opt;
bool default_hostname, default_name;
default_hostname = default_name = true;
jpcnt = INIT_PARAMCOUNT;
jp = malloc(jpcnt * sizeof(*jp));
if (jp == NULL)
err(2, "malloc");
jailparam_add("persist", "true");
jailparam_add("allow.mount", "true");
jailparam_add("allow.mount.devfs", "true");
jailparam_add("enforce_statfs", "1");
while ((opt = getopt(argc, argv, "o:")) != -1) {
switch (opt) {
case 'o':
if (jailparam_addarg(optarg)) {
/*
* optarg has been modified to null terminate
* at the assignment operator.
*/
if (strcmp(optarg, "name") == 0)
default_name = false;
if (strcmp(optarg, "host.hostname") == 0)
default_hostname = false;
}
break;
default:
fprintf(stderr, "bectl jail: unknown option '-%c'\n",
optopt);
return (usage(false));
}
}
argc -= optind;
argv += optind;
/* struct jail be_jail = { 0 }; */
if (argc < 1) {
fprintf(stderr, "bectl jail: missing boot environment name\n");
return (usage(false));
}
if (argc > 2) {
fprintf(stderr, "bectl jail: too many arguments\n");
return (usage(false));
}
bootenv = argv[0];
/*
* XXX TODO: if its already mounted, perhaps there should be a flag to
* indicate its okay to proceed??
*/
if (*mnt_loc == '\0')
mountpoint = NULL;
else
mountpoint = mnt_loc;
if (be_mount(be, bootenv, mountpoint, 0, mnt_loc) != BE_ERR_SUCCESS) {
fprintf(stderr, "could not mount bootenv\n");
return (1);
}
if (default_name)
jailparam_add("name", bootenv);
if (default_hostname)
jailparam_add("host.hostname", bootenv);
/*
* This is our indicator that path was not set by the user, so we'll use
* the path that libbe generated for us.
*/
if (mountpoint == NULL)
jailparam_add("path", mnt_loc);
jid = jailparam_set(jp, jpused, JAIL_CREATE | JAIL_ATTACH);
if (jid == -1) {
fprintf(stderr, "unable to create jail. error: %d\n", errno);
return (1);
}
jailparam_free(jp, jpused);
free(jp);
/* We're attached within the jail... good bye! */
chdir("/");
execl("/bin/sh", "/bin/sh", NULL);
return (0);
}
static int
bectl_search_jail_paths(const char *mnt)
{
char jailpath[MAXPATHLEN + 1];
int jid;
jid = 0;
(void)mnt;
while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath,
NULL)) != -1) {
if (strcmp(jailpath, mnt) == 0)
return (jid);
}
return (-1);
}
/*
* Locate a jail based on an arbitrary identifier. This may be either a name,
* a jid, or a BE name. Returns the jid or -1 on failure.
*/
static int
bectl_locate_jail(const char *ident)
{
nvlist_t *belist, *props;
char *mnt;
int jid;
/* Try the easy-match first */
jid = jail_getid(ident);
if (jid != -1)
return (jid);
/* Attempt to try it as a BE name, first */
if (be_prop_list_alloc(&belist) != 0)
return (-1);
if (be_get_bootenv_props(be, belist) != 0)
return (-1);
if (nvlist_lookup_nvlist(belist, ident, &props) == 0) {
/* We'll attempt to resolve the jid by way of mountpoint */
if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) {
jid = bectl_search_jail_paths(mnt);
be_prop_list_free(belist);
return (jid);
}
be_prop_list_free(belist);
}
return (-1);
}
int
bectl_cmd_unjail(int argc, char *argv[])
{
char path[MAXPATHLEN + 1];
char *cmd, *name, *target;
int jid;
/* Store alias used */
cmd = argv[0];
if (argc != 2) {
fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
return (usage(false));
}
target = argv[1];
/* Locate the jail */
if ((jid = bectl_locate_jail(target)) == -1) {
fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd,
target);
return (1);
}
bzero(&path, MAXPATHLEN + 1);
name = jail_getname(jid);
if (jail_getv(0, "name", name, "path", path, NULL) != jid) {
free(name);
fprintf(stderr,
"bectl %s: failed to get path for jail requested by '%s'\n",
cmd, target);
return (1);
}
free(name);
if (be_mounted_at(be, path, NULL) != 0) {
fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n",
cmd, target);
return (1);
}
jail_remove(jid);
unmount(path, 0);
return (0);
}