Allow to generate and write a comment into ~/.ssh/authorized_keys

This commit is contained in:
reykfloeter 2019-06-05 21:39:05 +02:00
parent c5c1705cd7
commit b8ae4a13fc
4 changed files with 109 additions and 17 deletions

View file

@ -23,6 +23,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <pwd.h>
#include <err.h> #include <err.h>
#include "main.h" #include "main.h"
@ -719,7 +720,7 @@ azure_getovfenv(struct system_config *sc)
} }
if ((xe = xml_findl(&xp->xe_head, "UserPassword", NULL)) != NULL) { if ((xe = xml_findl(&xp->xe_head, "UserPassword", NULL)) != NULL) {
if ((sc->sc_password = calloc(1, 128)) == NULL) { if ((sc->sc_password_hash = calloc(1, _PASSWORD_LEN)) == NULL) {
log_debug("%s: password failed", __func__); log_debug("%s: password failed", __func__);
goto done; goto done;
} }
@ -727,13 +728,14 @@ azure_getovfenv(struct system_config *sc)
str = strndup(xe->xe_data, xe->xe_datalen); str = strndup(xe->xe_data, xe->xe_datalen);
if (str == NULL || if (str == NULL ||
crypt_newhash(str, "bcrypt,a", crypt_newhash(str, "bcrypt,a",
sc->sc_password, 128) != 0) { sc->sc_password_hash, _PASSWORD_LEN) != 0) {
log_debug("%s: password hashing failed", __func__); log_debug("%s: password hashing failed", __func__);
free(sc->sc_password); free(sc->sc_password_hash);
sc->sc_password = NULL; sc->sc_password_hash = NULL;
free(str); free(str);
goto done; goto done;
} }
explicit_bzero(str, xe->xe_datalen);
free(str); free(str);
/* Replace unencrypted password with hash */ /* Replace unencrypted password with hash */
@ -743,11 +745,11 @@ azure_getovfenv(struct system_config *sc)
/* Update element for xml_print() below */ /* Update element for xml_print() below */
explicit_bzero(xe->xe_data, xe->xe_datalen); explicit_bzero(xe->xe_data, xe->xe_datalen);
free(xe->xe_data); free(xe->xe_data);
xe->xe_data = strdup(sc->sc_password); xe->xe_data = strdup(sc->sc_password_hash);
xe->xe_datalen = strlen(sc->sc_password); xe->xe_datalen = strlen(sc->sc_password_hash);
} else if ((xe = xml_findl(&xp->xe_head, } else if ((xe = xml_findl(&xp->xe_head,
"UserPasswordHash", NULL)) != NULL) { "UserPasswordHash", NULL)) != NULL) {
if ((sc->sc_password = if ((sc->sc_password_hash =
get_word(xe->xe_data, xe->xe_datalen)) != NULL) { get_word(xe->xe_data, xe->xe_datalen)) != NULL) {
log_debug("%s: password hash failed", __func__); log_debug("%s: password hash failed", __func__);
goto done; goto done;

View file

@ -23,6 +23,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm cloud-agent .Nm cloud-agent
.Op Fl nuv .Op Fl nuv
.Op Fl p Ar length
.Op Fl r Ar rootdisk .Op Fl r Ar rootdisk
.Op Fl t Ar timeout .Op Fl t Ar timeout
.Op Fl U Ar username .Op Fl U Ar username
@ -35,6 +36,17 @@ environments, including Microsoft Azure and Amazon AWS.
.Pp .Pp
The options are as follows: The options are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl p Ar length
Generate and set a random password for the default user.
The password will be written in its plain form into the
.Pa ~/.ssh/authorized_keys
file.
This allows to use the
.Xr doas 1
command to gain root privileges.
The minimum
.Ar length
is 8 characters and the default is an empty password.
.It Fl n .It Fl n
Do not configure the system and skip the provisioning step. Do not configure the system and skip the provisioning step.
.It Fl t Ar timeout .It Fl t Ar timeout
@ -78,6 +90,8 @@ dhcp
.Ed .Ed
.Sh FILES .Sh FILES
.Bl -tag -width "/usr/local/libexec/cloud-agentX" -compact .Bl -tag -width "/usr/local/libexec/cloud-agentX" -compact
.It Pa ~/.ssh/authorized_keys
The location of the agent-configured SSH public keys and optional password.
.It Pa /usr/local/libexec/cloud-agent .It Pa /usr/local/libexec/cloud-agent
The agent itself. The agent itself.
.It Pa /usr/local/bin/cms .It Pa /usr/local/bin/cms

View file

@ -37,6 +37,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <poll.h> #include <poll.h>
#include <pwd.h>
#include <err.h> #include <err.h>
#include "main.h" #include "main.h"
@ -360,13 +361,19 @@ agent_free(struct system_config *sc)
log_debug("%s: unmounted %s", __func__, sc->sc_cdrom); log_debug("%s: unmounted %s", __func__, sc->sc_cdrom);
} }
free(sc->sc_args); if (sc->sc_password_plain != NULL) {
/* XXX can be removed with calloc_conceal() post-6.6 */
explicit_bzero(sc->sc_password_plain,
strlen(sc->sc_password_plain));
free(sc->sc_password_plain);
}
free(sc->sc_password_hash);
free(sc->sc_hostname); free(sc->sc_hostname);
free(sc->sc_username); free(sc->sc_username);
free(sc->sc_password);
free(sc->sc_userdata); free(sc->sc_userdata);
free(sc->sc_endpoint); free(sc->sc_endpoint);
free(sc->sc_instance); free(sc->sc_instance);
free(sc->sc_args);
close(sc->sc_nullfd); close(sc->sc_nullfd);
while ((ssh = TAILQ_FIRST(&sc->sc_pubkeys))) { while ((ssh = TAILQ_FIRST(&sc->sc_pubkeys))) {
@ -628,6 +635,7 @@ agent_pf(struct system_config *sc, int open)
static int static int
agent_configure(struct system_config *sc) agent_configure(struct system_config *sc)
{ {
char pwbuf[_PASSWORD_LEN + 2];
struct ssh_pubkey *ssh; struct ssh_pubkey *ssh;
char *str1, *str2; char *str1, *str2;
@ -669,18 +677,30 @@ agent_configure(struct system_config *sc)
} }
/* password */ /* password */
if (sc->sc_password == NULL) { if (sc->sc_password_hash == NULL) {
if (asprintf(&str2, "permit keepenv nopass %s as root\n" if (asprintf(&str2, "permit keepenv nopass %s as root\n"
"permit keepenv nopass root\n", sc->sc_username) == -1) "permit keepenv nopass root\n", sc->sc_username) == -1)
str2 = NULL; str2 = NULL;
} else { } else {
if (shell("usermod", "-p", sc->sc_password, if (shell("usermod", "-p", sc->sc_password_hash,
sc->sc_username, NULL) != 0) sc->sc_username, NULL) != 0)
log_warnx("password failed"); log_warnx("password failed");
if (asprintf(&str2, "permit keepenv persist %s as root\n" if (asprintf(&str2, "permit keepenv persist %s as root\n"
"permit keepenv nopass root\n", sc->sc_username) == -1) "permit keepenv nopass root\n", sc->sc_username) == -1)
str2 = NULL; str2 = NULL;
/* write generated password as comment to authorized_keys */
if (sc->sc_password_plain != NULL) {
snprintf(pwbuf, sizeof(pwbuf), "# %s",
sc->sc_password_plain);
if (fileout(pwbuf, "w",
"%s/%s/.ssh/authorized_keys",
strcmp("root", sc->sc_username) == 0 ? "" : "/home",
sc->sc_username) != 0)
log_warnx("password comment failed");
explicit_bzero(pwbuf, sizeof(pwbuf));
}
} }
/* doas */ /* doas */
@ -690,7 +710,7 @@ agent_configure(struct system_config *sc)
free(str2); free(str2);
/* ssh configuration */ /* ssh configuration */
if (sc->sc_password == NULL && !TAILQ_EMPTY(&sc->sc_pubkeys)) if (sc->sc_password_hash == NULL && !TAILQ_EMPTY(&sc->sc_pubkeys))
str1 = "/PasswordAuthentication/" str1 = "/PasswordAuthentication/"
"s/.*/PasswordAuthentication no/"; "s/.*/PasswordAuthentication no/";
else else
@ -1096,8 +1116,8 @@ usage(void)
{ {
extern char *__progname; extern char *__progname;
fprintf(stderr, "usage: %s [-nuv] [-r rootdisk] [-t 3] [-U puffy] " fprintf(stderr, "usage: %s [-nuv] [-p length] [-r rootdisk] "
"interface\n", __progname); "[-t 3] [-U puffy] interface\n", __progname);
exit(1); exit(1);
} }
@ -1131,23 +1151,65 @@ get_args(int argc, char *const *argv)
return (args); return (args);
} }
static char *
pwgen(size_t len, char **hash)
{
char *password;
size_t i, alphabet_len;
const char *alphabet =
"0123456789_"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
alphabet_len = strlen(alphabet);
*hash = NULL;
/* XXX use calloc_conceal() post-6.6 */
if ((password = calloc(1, len + 1)) == NULL)
return (NULL);
/* Simple password generator */
for (i = 0; i < len; i++)
password[i] = alphabet[arc4random_uniform(alphabet_len)];
if ((*hash = calloc(1, _PASSWORD_LEN)) == NULL) {
freezero(password, len + 1);
return (NULL);
}
if (crypt_newhash(password, "bcrypt,a",
*hash, _PASSWORD_LEN) != 0) {
freezero(password, len + 1);
freezero(*hash, _PASSWORD_LEN);
password = NULL;
*hash = NULL;
}
return (password);
}
int int
main(int argc, char *const *argv) main(int argc, char *const *argv)
{ {
struct system_config *sc; struct system_config *sc;
int verbose = 0, dryrun = 0, unconfigure = 0; int verbose = 0, dryrun = 0, unconfigure = 0;
int ch, ret, timeout = CONNECT_TIMEOUT; int genpw = 0, ch, ret, timeout = CONNECT_TIMEOUT;
const char *error = NULL, *rootdisk = NULL; const char *error = NULL, *rootdisk = NULL;
char *args, *username = NULL; char *args, *username = NULL;
if ((args = get_args(argc, argv)) == NULL) if ((args = get_args(argc, argv)) == NULL)
fatalx("failed to save args"); fatalx("failed to save args");
while ((ch = getopt(argc, argv, "nr:t:U:uv")) != -1) { while ((ch = getopt(argc, argv, "np:r:t:U:uv")) != -1) {
switch (ch) { switch (ch) {
case 'n': case 'n':
dryrun = 1; dryrun = 1;
break; break;
case 'p':
genpw = strtonum(optarg, 8, 8192, &error);
if (error != NULL)
fatalx("invalid password length: %s", error);
break;
case 'r': case 'r':
rootdisk = optarg; rootdisk = optarg;
break; break;
@ -1217,6 +1279,19 @@ main(int argc, char *const *argv)
else else
ret = openstack(sc); ret = openstack(sc);
if (genpw) {
if (sc->sc_password_hash != NULL)
log_debug("%s: user password hash: %s", __func__,
sc->sc_password_hash);
else if ((sc->sc_password_plain = pwgen(genpw,
&sc->sc_password_hash)) != NULL) {
if (log_getverbose() > 2)
log_debug("%s: generated password: %s",
__func__, sc->sc_password_plain);
} else
log_warnx("failed to generate password");
}
/* Debug userdata */ /* Debug userdata */
if (sc->sc_dryrun && sc->sc_userdata) { if (sc->sc_dryrun && sc->sc_userdata) {
if (agent_userdata(sc, sc->sc_userdata, if (agent_userdata(sc, sc->sc_userdata,

View file

@ -72,7 +72,8 @@ struct system_config {
char *sc_hostname; char *sc_hostname;
char *sc_username; char *sc_username;
char *sc_password; char *sc_password_plain;
char *sc_password_hash;
char *sc_pubkey; char *sc_pubkey;
char *sc_userdata; char *sc_userdata;
char *sc_endpoint; char *sc_endpoint;