Compare commits
No commits in common. "master" and "fix-cms" have entirely different histories.
14 changed files with 120 additions and 892 deletions
69
CHANGELOG.md
69
CHANGELOG.md
|
@ -1,69 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## v1.0 (2019-11-29)
|
|
||||||
|
|
||||||
* Append `/etc/resolv.conf.tail` to `/etc/resolv.conf` if it exists.
|
|
||||||
* Fixed usage.
|
|
||||||
|
|
||||||
## v0.9 (2019-06-26)
|
|
||||||
|
|
||||||
* Added support for `-c` to specify the probing order of different cloud stacks.
|
|
||||||
* Added support for OpenNebula's START_SCRIPT methods (user-data alike).
|
|
||||||
* Added support for OpenNebula's USERNAME method.
|
|
||||||
* Added support for generating a default user password and writing it into
|
|
||||||
`~/.ssh/authorized_keys`.
|
|
||||||
* Fixed handling of OpenNebula SSH_PUBLIC_KEY entries with multiple keys.
|
|
||||||
* Improved documentation, added `CHANGELOG.md` file.
|
|
||||||
|
|
||||||
## v0.8 (2019-06-02)
|
|
||||||
|
|
||||||
* Added support for growing the root disk and its last partition (optional).
|
|
||||||
* Fixed OpenStack support.
|
|
||||||
* Fixed compilation with LibreSSL on OpenBSD 6.5 or newer.
|
|
||||||
* Fixed probing order and OpenStack with `169.254.169.254` as the endpoint IP.
|
|
||||||
* Improved OpenNebula support.
|
|
||||||
|
|
||||||
## v0.7 (2018-08-15)
|
|
||||||
|
|
||||||
* Added initial support for OpenNebula contextualization.
|
|
||||||
* Added support for setting a custom login user or "root" with `-U`.
|
|
||||||
* Added support for writing `resolv.conf` and static network configuration.
|
|
||||||
* Fixed the generated pf rule that is loaded during cloud-agent operation.
|
|
||||||
|
|
||||||
## v0.6 (2018-05-15)
|
|
||||||
|
|
||||||
* Fixed compilation with (old) OpenSSL releases.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## v0.5 (2018-05-08)
|
|
||||||
|
|
||||||
* Fixed the user-data script by loading it from /etc/rc.user-data.
|
|
||||||
|
|
||||||
## v0.4 (2018-05-08)
|
|
||||||
|
|
||||||
* Added support for user-data that is not base64-encoded.
|
|
||||||
|
|
||||||
## v0.3 (2018-05-08)
|
|
||||||
|
|
||||||
* Added support for user-data scripts.
|
|
||||||
* Make the public key optional for stacks that supply a password (e.g. Azure).
|
|
||||||
|
|
||||||
## v0.2.2 (2018-05-07)
|
|
||||||
|
|
||||||
* Fixed issues in the v0.2.1 release.
|
|
||||||
|
|
||||||
## v0.2.2 (2018-05-07)
|
|
||||||
|
|
||||||
* Fixed issues in the v0.2 release.
|
|
||||||
|
|
||||||
## v0.2 (2018-01-10)
|
|
||||||
|
|
||||||
* Added support for OpenStack and its JSON-based meta data.
|
|
||||||
* Added support for Apache CloudStack.
|
|
||||||
* Try to get meta data from `dhcp-server-identifier` instead of
|
|
||||||
`169.254.169.254`.
|
|
||||||
|
|
||||||
## v0.1 (2017-07-03)
|
|
||||||
|
|
||||||
* Initial release with support for Microsoft Azure and Amazon AWS EC2.
|
|
|
@ -6,10 +6,10 @@ License
|
||||||
|
|
||||||
* `cloud-agent` is free software under OpenBSD's ISC-style license.
|
* `cloud-agent` is free software under OpenBSD's ISC-style license.
|
||||||
* Most of the code has been written by Reyk Floeter <reyk@openbsd.org>
|
* Most of the code has been written by Reyk Floeter <reyk@openbsd.org>
|
||||||
* The {http,json}.[ch] files were written by Kristaps Dzonsons <kristaps@bsd.lv>
|
* The http.[ch] files have been written by Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
* Please refer to the individual source files for other copyright holders!
|
* Please refer to the individual source files for other copyright holders!
|
||||||
|
|
||||||
> Copyright (c) 2017, 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
> Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||||
>
|
>
|
||||||
> Permission to use, copy, modify, and distribute this software for any
|
> Permission to use, copy, modify, and distribute this software for any
|
||||||
> purpose with or without fee is hereby granted, provided that the above
|
> purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -26,5 +26,5 @@ License
|
||||||
`cms/`
|
`cms/`
|
||||||
------
|
------
|
||||||
|
|
||||||
* The CMS code is from OpenSSL and/or LibreSSL.
|
* The CMS code is from the OpenSSL and/or LibreSSL.
|
||||||
* Please refer to the individual source files for other copyright holders!
|
* Please refer to the individual source files for other copyright holders!
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -1,13 +1,10 @@
|
||||||
#
|
#
|
||||||
# The Azure agents needs CMS to obtain the SSH public keys.
|
# The Azure agents needs CMS to obtain the SSH public keys.
|
||||||
# LibreSSL has removed CMS, so either use OpenSSL to decrypt CMS
|
# LibreSSL has removed CMS, so either use OpenSSL to decrypt CMS
|
||||||
# messages or compile the old CMS code for LibreSSL. Or use
|
# messages or compile the old CMS code for LibreSSL.
|
||||||
# CMS that has returned to newer versions of LibreSSL.
|
|
||||||
#
|
#
|
||||||
.ifdef USE_OPENSSL
|
.ifdef USE_OPENSSL
|
||||||
MAKE_FLAGS+= USE_OPENSSL=1
|
MAKE_FLAGS+= USE_OPENSSL=1
|
||||||
.elifdef USE_LIBRESSL_CMS
|
|
||||||
MAKE_FLAGS+= USE_LIBRESSL_CMS=1
|
|
||||||
.else
|
.else
|
||||||
SUBDIR= cms
|
SUBDIR= cms
|
||||||
.endif
|
.endif
|
||||||
|
|
26
README.md
26
README.md
|
@ -23,40 +23,26 @@ has removed CMS which is required by Azure.
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
See the [cloud-agent(8)](cloud-agent.md) documentation for more
|
Installation is easy, `cloud-agent` detects the cloud type automatically.
|
||||||
information about the usage.
|
|
||||||
|
|
||||||
Basic installation is easy, `cloud-agent` detects the cloud type
|
|
||||||
automatically.
|
|
||||||
|
|
||||||
* On Microsoft Azure, create a file `/etc/hostname.hvn0`
|
* On Microsoft Azure, create a file `/etc/hostname.hvn0`
|
||||||
|
|
||||||
* On Amazon AWS, create a file `/etc/hostname.xnf0`
|
* On Amazon AWS, create a file `/etc/hostname.xnf0`
|
||||||
|
|
||||||
* On CloudStack, such as Exoscale, create a file `/etc/hostname.vio0`
|
* On Exoscale, create a file `/etc/hostname.vio0`
|
||||||
|
|
||||||
* On OpenBSD VMM (with meta-data), create a file `/etc/hostname.vio0`
|
* On OpenBSD VMM (with meta-data), create a file `/etc/hostname.vio0`
|
||||||
|
|
||||||
* On OpenStack/VMware, create a file `/etc/hostname.vmx0`
|
* On OpenStack/VMware, create a file `/etc/hostname.vmx0`
|
||||||
|
|
||||||
* The content of the file is identical for all of the above:
|
* On OpenNebula, create a file `/etc/hostname.if`
|
||||||
|
where _if_ is the name of your primary interface.
|
||||||
|
|
||||||
|
* The content of the file is identical for all of them:
|
||||||
|
|
||||||
dhcp
|
dhcp
|
||||||
!/usr/local/libexec/cloud-agent "\$if"
|
!/usr/local/libexec/cloud-agent "\$if"
|
||||||
|
|
||||||
* On OpenNebula, such as Data Center Light, create a file `/etc/hostname.if`
|
|
||||||
where _if_ is the name of your primary interface.
|
|
||||||
The `dhcp` line should be ommitted in the file:
|
|
||||||
|
|
||||||
!/usr/local/libexec/cloud-agent "\$if"
|
|
||||||
|
|
||||||
Releases
|
|
||||||
--------
|
|
||||||
|
|
||||||
See the [Changelog](CHANGELOG.md) for a summary of changes and
|
|
||||||
download the releases from the
|
|
||||||
[release page](https://github.com/reyk/cloud-agent/releases).
|
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
PROG= cloud-agent
|
PROG= cloud-agent
|
||||||
SRCS= http.c json.c jsmn.c log.c main.c xml.c
|
SRCS= http.c json.c jsmn.c log.c main.c xml.c
|
||||||
SRCS+= azure.c cloudinit.c opennebula.c openstack.c
|
SRCS+= azure.c cloudinit.c opennebula.c openstack.c
|
||||||
SRCS+= growdisk.c
|
|
||||||
BINDIR= /usr/local/libexec
|
BINDIR= /usr/local/libexec
|
||||||
MANDIR= /usr/local/man/man
|
MANDIR= /usr/local/man/man
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
* Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -23,11 +23,8 @@
|
||||||
#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 <openssl/opensslv.h>
|
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "xml.h"
|
#include "xml.h"
|
||||||
|
@ -65,22 +62,16 @@ azure(struct system_config *sc)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
if (sc->sc_state == STATE_INIT) {
|
sc->sc_stack = "azure";
|
||||||
free(sc->sc_username);
|
|
||||||
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
|
||||||
log_warnx("failed to set default user");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply defaults */
|
/* Apply defaults */
|
||||||
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
free(sc->sc_username);
|
||||||
sc->sc_priv = &az_config;
|
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
||||||
sc->sc_state = STATE_DHCP;
|
log_warnx("failed to set default user");
|
||||||
return (-1);
|
goto fail;
|
||||||
}
|
}
|
||||||
|
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
||||||
/* Don't try other endpoints */
|
sc->sc_priv = &az_config;
|
||||||
sc->sc_state = STATE_DONE;
|
|
||||||
|
|
||||||
if (azure_getovfenv(sc) != 0) {
|
if (azure_getovfenv(sc) != 0) {
|
||||||
log_warnx("failed to get ovf-env.xml");
|
log_warnx("failed to get ovf-env.xml");
|
||||||
|
@ -443,16 +434,13 @@ azure_certificates(struct system_config *sc)
|
||||||
|
|
||||||
fd = disable_output(sc, STDERR_FILENO);
|
fd = disable_output(sc, STDERR_FILENO);
|
||||||
|
|
||||||
#if defined(USE_OPENSSL)
|
#ifdef USE_OPENSSL
|
||||||
/*
|
/*
|
||||||
* XXX Now comes the part that needs CMS which is only
|
* XXX Now comes the part that needs CMS which is only
|
||||||
* XXX present in OpenSSL but got removed from LibreSSL.
|
* XXX present in OpenSSL but got removed from LibreSSL.
|
||||||
*/
|
*/
|
||||||
log_debug("%s: running openssl cms", __func__);
|
log_debug("%s: running openssl cms", __func__);
|
||||||
if (shell("/usr/local/bin/eopenssl", "cms", /* )) */
|
if (shell("/usr/local/bin/eopenssl", "cms", /* )) */
|
||||||
#elif defined(USE_LIBRESSL_CMS) || LIBRESSL_VERSION_NUMBER > 0x3000200fL
|
|
||||||
/* And CMS returned to LibreSSL! */
|
|
||||||
if (shell("/usr/bin/openssl", "cms", /* )) */
|
|
||||||
#else
|
#else
|
||||||
if (shell("/usr/local/bin/cms",
|
if (shell("/usr/local/bin/cms",
|
||||||
#endif
|
#endif
|
||||||
|
@ -731,7 +719,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_hash = calloc(1, _PASSWORD_LEN)) == NULL) {
|
if ((sc->sc_password = calloc(1, 128)) == NULL) {
|
||||||
log_debug("%s: password failed", __func__);
|
log_debug("%s: password failed", __func__);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -739,14 +727,13 @@ 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_hash, _PASSWORD_LEN) != 0) {
|
sc->sc_password, 128) != 0) {
|
||||||
log_debug("%s: password hashing failed", __func__);
|
log_debug("%s: password hashing failed", __func__);
|
||||||
free(sc->sc_password_hash);
|
free(sc->sc_password);
|
||||||
sc->sc_password_hash = NULL;
|
sc->sc_password = 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 */
|
||||||
|
@ -756,11 +743,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_hash);
|
xe->xe_data = strdup(sc->sc_password);
|
||||||
xe->xe_datalen = strlen(sc->sc_password_hash);
|
xe->xe_datalen = strlen(sc->sc_password);
|
||||||
} 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_hash =
|
if ((sc->sc_password =
|
||||||
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;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" $OpenBSD: mdoc.template,v 1.15 2014/03/31 00:09:54 dlg Exp $
|
.\" $OpenBSD: mdoc.template,v 1.15 2014/03/31 00:09:54 dlg Exp $
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2017, 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
.\" Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||||
.\"
|
.\"
|
||||||
.\" Permission to use, copy, modify, and distribute this software for any
|
.\" Permission to use, copy, modify, and distribute this software for any
|
||||||
.\" purpose with or without fee is hereby granted, provided that the above
|
.\" purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -23,9 +23,6 @@
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm cloud-agent
|
.Nm cloud-agent
|
||||||
.Op Fl nuv
|
.Op Fl nuv
|
||||||
.Op Fl c Ar cloud Ns Op , Ns Ar cloud Ns ...
|
|
||||||
.Op Fl p Ar length
|
|
||||||
.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
|
||||||
.Ar interface
|
.Ar interface
|
||||||
|
@ -33,42 +30,10 @@
|
||||||
The
|
The
|
||||||
.Nm
|
.Nm
|
||||||
program manages the OpenBSD provisioning and VM interaction in cloud
|
program manages the OpenBSD provisioning and VM interaction in cloud
|
||||||
environments.
|
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 c Ar cloud Ns Op , Ns Ar cloud Ns ...
|
|
||||||
Probe a list of cloud stacks for provisioning in the specified order.
|
|
||||||
If this option is not specified,
|
|
||||||
.Nm
|
|
||||||
tries to detect the environment and possible cloud stacks automatically.
|
|
||||||
Supported
|
|
||||||
.Ar cloud
|
|
||||||
stacks are:
|
|
||||||
.Pp
|
|
||||||
.Bl -tag -width opennebula -offset indent -compact
|
|
||||||
.It Ic azure
|
|
||||||
Microsoft Azure
|
|
||||||
.It Ic cloudinit
|
|
||||||
Generic cloud-init
|
|
||||||
.It Ic ec2
|
|
||||||
Amazon AWS EC2
|
|
||||||
.It Ic opennebula
|
|
||||||
OpenNebula
|
|
||||||
.It Ic openstack
|
|
||||||
OpenStack
|
|
||||||
.El
|
|
||||||
.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
|
||||||
|
@ -88,10 +53,6 @@ configuration.
|
||||||
Using
|
Using
|
||||||
.Dq root
|
.Dq root
|
||||||
is supported, but not recommended.
|
is supported, but not recommended.
|
||||||
.It Fl r Ar rootdisk
|
|
||||||
Automatically grow the last
|
|
||||||
.Ox
|
|
||||||
FFS partition of the root disk to use all the available space.
|
|
||||||
.It Fl u
|
.It Fl u
|
||||||
Deprovision and unconfigure the system.
|
Deprovision and unconfigure the system.
|
||||||
This deletes keys, passwords, and logs files without asking for permission.
|
This deletes keys, passwords, and logs files without asking for permission.
|
||||||
|
@ -103,17 +64,14 @@ Enable
|
||||||
.Nm
|
.Nm
|
||||||
in the
|
in the
|
||||||
.Xr hostname.if 5
|
.Xr hostname.if 5
|
||||||
of the VM's primary networking interface and automatically the last
|
of the VM's primary networking interface:
|
||||||
partition of the root disk:
|
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
# cat /etc/hostname.hvn0
|
# cat /etc/hostname.hvn0
|
||||||
dhcp
|
dhcp
|
||||||
!/usr/local/libexec/cloud-agent -r sd0 "\e$if"
|
!/usr/local/libexec/cloud-agent "\$if"
|
||||||
.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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
* Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -34,26 +34,28 @@ static int cloudinit_fetch(struct system_config *);
|
||||||
int
|
int
|
||||||
ec2(struct system_config *sc)
|
ec2(struct system_config *sc)
|
||||||
{
|
{
|
||||||
if (sc->sc_state == STATE_INIT) {
|
free(sc->sc_username);
|
||||||
free(sc->sc_username);
|
if ((sc->sc_username = strdup("ec2-user")) == NULL ||
|
||||||
if ((sc->sc_username = strdup("ec2-user")) == NULL) {
|
(sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
|
||||||
log_warnx("failed to set default user");
|
log_warnx("failed to set defaults");
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
sc->sc_state = STATE_169;
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
return cloudinit_fetch(sc);
|
|
||||||
|
sc->sc_stack = "ec2";
|
||||||
|
return (cloudinit_fetch(sc));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cloudinit(struct system_config *sc)
|
cloudinit(struct system_config *sc)
|
||||||
{
|
{
|
||||||
if (sc->sc_state == STATE_INIT) {
|
if ((dhcp_getendpoint(sc) == -1) &&
|
||||||
sc->sc_state = STATE_DHCP;
|
(sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
|
||||||
|
log_warnx("failed to set defaults");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
return cloudinit_fetch(sc);
|
|
||||||
|
sc->sc_stack = "cloudinit";
|
||||||
|
return (cloudinit_fetch(sc));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
173
agent/growdisk.c
173
agent/growdisk.c
|
@ -1,173 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 Reyk Floeter <reyk@openbsd.org>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/param.h> /* DEV_BSIZE */
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/dkio.h>
|
|
||||||
#define DKTYPENAMES
|
|
||||||
#include <sys/disklabel.h>
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <util.h>
|
|
||||||
#include <err.h>
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
#define MEG(_n) ((_n) * DEV_BSIZE / 1024 / 1024)
|
|
||||||
|
|
||||||
static uint16_t dkcksum(struct disklabel *);
|
|
||||||
|
|
||||||
static uint16_t
|
|
||||||
dkcksum(struct disklabel *lp)
|
|
||||||
{
|
|
||||||
uint16_t *start, *end, sum;
|
|
||||||
start = (uint16_t *)lp;
|
|
||||||
end = (uint16_t *)&lp->d_partitions[lp->d_npartitions];
|
|
||||||
for (sum = 0; start < end;)
|
|
||||||
sum ^= *start++;
|
|
||||||
return (sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
growdisk(struct system_config *sc)
|
|
||||||
{
|
|
||||||
char c, last_part = 0, *out = NULL, *path = NULL;
|
|
||||||
int ret = -1, i, errfd, outfd;
|
|
||||||
uint64_t bend, psize;
|
|
||||||
struct partition *pp, *p = NULL;
|
|
||||||
struct disklabel lp;
|
|
||||||
uint16_t cksum;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Grow the OpenBSD MBR partition
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* XXX this is a bit ugly but easier to do */
|
|
||||||
if (!sc->sc_dryrun &&
|
|
||||||
shellout("e 3\n\n\n\n*\nw\nq\n", &out,
|
|
||||||
"fdisk", "-e", sc->sc_rootdisk, NULL) != 0) {
|
|
||||||
log_warnx("failed to grow OpenBSD partition");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
free(out);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Grow the last partition in the disklabel
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ((fd = opendev(sc->sc_rootdisk,
|
|
||||||
O_RDWR, OPENDEV_PART, NULL)) == -1) {
|
|
||||||
log_warn("failed to open %s", sc->sc_rootdisk);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(fd, DIOCGDINFO, &lp) == -1) {
|
|
||||||
log_warn("failed to get disklabel");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lp.d_magic != DISKMAGIC || lp.d_magic2 != DISKMAGIC) {
|
|
||||||
log_warnx("invalid disklabel magic bytes");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
cksum = lp.d_checksum;
|
|
||||||
lp.d_checksum = 0;
|
|
||||||
|
|
||||||
if (dkcksum(&lp) != cksum) {
|
|
||||||
log_warnx("invalid disklabel checksum");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
pp = lp.d_partitions;
|
|
||||||
for (i = 0, pp = lp.d_partitions; i < lp.d_npartitions; i++, pp++) {
|
|
||||||
if (!DL_GETPSIZE(pp))
|
|
||||||
continue;
|
|
||||||
c = 'a' + i;
|
|
||||||
if (pp->p_fstype == FS_BSDFFS) {
|
|
||||||
last_part = c;
|
|
||||||
p = pp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_part == 0) {
|
|
||||||
log_warnx("last BSD partition not found");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
bend = DL_GETDSIZE(&lp) - DL_GETBSTART(&lp);
|
|
||||||
psize = bend - DL_GETPOFFSET(p);
|
|
||||||
|
|
||||||
/* Only grow, but never shring the disk */
|
|
||||||
if (bend <= DL_GETBEND(&lp) && psize <= DL_GETPSIZE(p)) {
|
|
||||||
log_debug("%s: not growing %s%c, size is %lluMB",
|
|
||||||
__func__, sc->sc_rootdisk, last_part, MEG(psize));
|
|
||||||
ret = 0;
|
|
||||||
} else {
|
|
||||||
log_info("growing %s%c from %lluMB to %lluMB",
|
|
||||||
sc->sc_rootdisk, last_part,
|
|
||||||
MEG(DL_GETPSIZE(p)), MEG(psize));
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
if (sc->sc_dryrun || ret == 0) {
|
|
||||||
ret = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update OpenBSD boundaries */
|
|
||||||
DL_SETBEND(&lp, bend);
|
|
||||||
|
|
||||||
/* Update the size of the last partition */
|
|
||||||
DL_SETPSIZE(p, psize);
|
|
||||||
|
|
||||||
lp.d_checksum = dkcksum(&lp);
|
|
||||||
|
|
||||||
if (ioctl(fd, DIOCWDINFO, &lp) == -1) {
|
|
||||||
log_warn("failed to write disklabel");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Grow the filesystem
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (asprintf(&path, "/dev/%s%c", sc->sc_rootdisk, last_part) == -1)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
errfd = disable_output(sc, STDERR_FILENO);
|
|
||||||
outfd = disable_output(sc, STDOUT_FILENO);
|
|
||||||
|
|
||||||
(void)shell("umount", "-f", path, NULL);
|
|
||||||
(void)shell("growfs", "-yq", path, NULL);
|
|
||||||
if ((ret = shell("fsck", "-y", path, NULL)) != 0)
|
|
||||||
ret = -1;
|
|
||||||
(void)shell("mount", "-a", "-t", "nonfs,vnd", NULL);
|
|
||||||
|
|
||||||
enable_output(sc, STDERR_FILENO, errfd);
|
|
||||||
enable_output(sc, STDOUT_FILENO, outfd);
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
done:
|
|
||||||
free(path);
|
|
||||||
close(fd);
|
|
||||||
return (ret);
|
|
||||||
}
|
|
311
agent/main.c
311
agent/main.c
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
* Copyright (c) 2017, 2018 Reyk Floeter <reyk@openbsd.org>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -37,26 +37,22 @@
|
||||||
#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"
|
||||||
#include "xml.h"
|
#include "xml.h"
|
||||||
|
|
||||||
__dead void usage(void);
|
__dead void usage(void);
|
||||||
static struct system_config
|
static struct system_config *agent_init(const char *, int, int);
|
||||||
*agent_init(const char *, const char *, int, int);
|
static int agent_configure(struct system_config *);
|
||||||
static int agent_configure(struct system_config *);
|
static int agent_network(struct system_config *);
|
||||||
static int agent_network(struct system_config *);
|
static void agent_free(struct system_config *);
|
||||||
static void agent_free(struct system_config *);
|
static int agent_pf(struct system_config *, int);
|
||||||
static int agent_pf(struct system_config *, int);
|
static int agent_userdata(const unsigned char *, size_t);
|
||||||
static int agent_userdata(struct system_config *,
|
static void agent_unconfigure(void);
|
||||||
const unsigned char *, size_t);
|
static char *metadata_parse(char *, size_t, enum strtype);
|
||||||
static void agent_unconfigure(void);
|
|
||||||
static char *metadata_parse(char *, size_t, enum strtype);
|
|
||||||
|
|
||||||
static int agent_timeout;
|
static int agent_timeout;
|
||||||
static char *cloudnames[] = CLOUDNAMES;
|
|
||||||
|
|
||||||
int
|
int
|
||||||
shell(const char *arg, ...)
|
shell(const char *arg, ...)
|
||||||
|
@ -310,7 +306,7 @@ get_word(const unsigned char *ptr, size_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct system_config *
|
static struct system_config *
|
||||||
agent_init(const char *ifname, const char *rootdisk, int dryrun, int timeout)
|
agent_init(const char *ifname, int dryrun, int timeout)
|
||||||
{
|
{
|
||||||
struct system_config *sc;
|
struct system_config *sc;
|
||||||
int fd, ret;
|
int fd, ret;
|
||||||
|
@ -322,7 +318,6 @@ agent_init(const char *ifname, const char *rootdisk, int dryrun, int timeout)
|
||||||
sc->sc_cdrom = "/dev/cd0c";
|
sc->sc_cdrom = "/dev/cd0c";
|
||||||
sc->sc_dryrun = dryrun ? 1 : 0;
|
sc->sc_dryrun = dryrun ? 1 : 0;
|
||||||
sc->sc_timeout = agent_timeout = timeout < 1 ? -1 : timeout * 1000;
|
sc->sc_timeout = agent_timeout = timeout < 1 ? -1 : timeout * 1000;
|
||||||
sc->sc_rootdisk = rootdisk;
|
|
||||||
TAILQ_INIT(&sc->sc_pubkeys);
|
TAILQ_INIT(&sc->sc_pubkeys);
|
||||||
TAILQ_INIT(&sc->sc_netaddrs);
|
TAILQ_INIT(&sc->sc_netaddrs);
|
||||||
|
|
||||||
|
@ -362,19 +357,13 @@ agent_free(struct system_config *sc)
|
||||||
log_debug("%s: unmounted %s", __func__, sc->sc_cdrom);
|
log_debug("%s: unmounted %s", __func__, sc->sc_cdrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sc->sc_password_plain != NULL) {
|
free(sc->sc_args);
|
||||||
/* 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))) {
|
||||||
|
@ -636,7 +625,6 @@ 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;
|
||||||
|
|
||||||
|
@ -678,30 +666,18 @@ agent_configure(struct system_config *sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* password */
|
/* password */
|
||||||
if (sc->sc_password_hash == NULL) {
|
if (sc->sc_password == 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_hash,
|
if (shell("usermod", "-p", sc->sc_password,
|
||||||
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 */
|
||||||
|
@ -711,7 +687,7 @@ agent_configure(struct system_config *sc)
|
||||||
free(str2);
|
free(str2);
|
||||||
|
|
||||||
/* ssh configuration */
|
/* ssh configuration */
|
||||||
if (sc->sc_password_hash == NULL && !TAILQ_EMPTY(&sc->sc_pubkeys))
|
if (sc->sc_password == NULL && !TAILQ_EMPTY(&sc->sc_pubkeys))
|
||||||
str1 = "/PasswordAuthentication/"
|
str1 = "/PasswordAuthentication/"
|
||||||
"s/.*/PasswordAuthentication no/";
|
"s/.*/PasswordAuthentication no/";
|
||||||
else
|
else
|
||||||
|
@ -735,7 +711,7 @@ agent_configure(struct system_config *sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sc->sc_userdata) {
|
if (sc->sc_userdata) {
|
||||||
if (agent_userdata(sc, sc->sc_userdata,
|
if (agent_userdata(sc->sc_userdata,
|
||||||
strlen(sc->sc_userdata)) != 0)
|
strlen(sc->sc_userdata)) != 0)
|
||||||
log_warnx("user-data failed");
|
log_warnx("user-data failed");
|
||||||
}
|
}
|
||||||
|
@ -759,8 +735,7 @@ agent_configure(struct system_config *sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
agent_userdata(struct system_config *sc,
|
agent_userdata(const unsigned char *userdata, size_t len)
|
||||||
const unsigned char *userdata, size_t len)
|
|
||||||
{
|
{
|
||||||
char *shebang = NULL, *str = NULL, *line = NULL;
|
char *shebang = NULL, *str = NULL, *line = NULL;
|
||||||
const char *file;
|
const char *file;
|
||||||
|
@ -783,7 +758,7 @@ agent_userdata(struct system_config *sc,
|
||||||
/* Decode user-data and call the function again */
|
/* Decode user-data and call the function again */
|
||||||
if ((str = calloc(1, len + 1)) == NULL ||
|
if ((str = calloc(1, len + 1)) == NULL ||
|
||||||
(len = b64_pton(userdata, str, len)) < 1 ||
|
(len = b64_pton(userdata, str, len)) < 1 ||
|
||||||
agent_userdata(sc, str, len) != 0) {
|
agent_userdata(str, len) != 0) {
|
||||||
log_warnx("failed to decode user-data");
|
log_warnx("failed to decode user-data");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -803,9 +778,6 @@ agent_userdata(struct system_config *sc,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sc->sc_dryrun)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* write user-data script into file */
|
/* write user-data script into file */
|
||||||
file = "/etc/rc.user-data";
|
file = "/etc/rc.user-data";
|
||||||
if (fileout(str, "w", file) != 0) {
|
if (fileout(str, "w", file) != 0) {
|
||||||
|
@ -838,7 +810,7 @@ agent_network(struct system_config *sc)
|
||||||
const char *family;
|
const char *family;
|
||||||
char domain[(NI_MAXHOST + 1) * 6 + 8]; /* up to 6 domains */
|
char domain[(NI_MAXHOST + 1) * 6 + 8]; /* up to 6 domains */
|
||||||
int has_domain = 0;
|
int has_domain = 0;
|
||||||
char ifidx[UINT16_MAX], *str;
|
char ifidx[UINT16_MAX];
|
||||||
const char *comment = "# Generated by cloud-agent";
|
const char *comment = "# Generated by cloud-agent";
|
||||||
|
|
||||||
if (!sc->sc_network)
|
if (!sc->sc_network)
|
||||||
|
@ -907,12 +879,6 @@ agent_network(struct system_config *sc)
|
||||||
if (has_domain)
|
if (has_domain)
|
||||||
fileout(domain, "a", "/etc/resolv.conf");
|
fileout(domain, "a", "/etc/resolv.conf");
|
||||||
|
|
||||||
/* append resolv.conf.tail if it exists */
|
|
||||||
if ((str = filein("r", "/etc/resolv.conf.tail")) != NULL) {
|
|
||||||
fileout(str, "a", "/etc/resolv.conf");
|
|
||||||
free(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,11 +908,9 @@ agent_unconfigure(void)
|
||||||
|
|
||||||
(void)fileout("permit keepenv persist :wheel as root\n"
|
(void)fileout("permit keepenv persist :wheel as root\n"
|
||||||
"permit keepenv nopass root\n", "w", "/etc/doas.conf");
|
"permit keepenv nopass root\n", "w", "/etc/doas.conf");
|
||||||
|
|
||||||
/* Remove cloud-instance file */
|
|
||||||
(void)unlink("/var/db/cloud-instance");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
metadata_parse(char *s, size_t sz, enum strtype type)
|
metadata_parse(char *s, size_t sz, enum strtype type)
|
||||||
{
|
{
|
||||||
|
@ -977,8 +941,7 @@ metadata(struct system_config *sc, const char *path, enum strtype type)
|
||||||
g = http_get(&sc->sc_addr, 1,
|
g = http_get(&sc->sc_addr, 1,
|
||||||
sc->sc_endpoint, 80, path, NULL, 0, NULL);
|
sc->sc_endpoint, 80, path, NULL, 0, NULL);
|
||||||
if (g != NULL)
|
if (g != NULL)
|
||||||
log_debug("%s: HTTP %d http://%s%s", __func__, g->code,
|
log_debug("%s: HTTP %d %s", __func__, g->code, path);
|
||||||
sc->sc_endpoint, path);
|
|
||||||
|
|
||||||
if (g != NULL && g->code == 200 && g->bodypartsz > 0)
|
if (g != NULL && g->code == 200 && g->bodypartsz > 0)
|
||||||
str = metadata_parse(g->bodypart, g->bodypartsz, type);
|
str = metadata_parse(g->bodypart, g->bodypartsz, type);
|
||||||
|
@ -1116,8 +1079,7 @@ dhcp_getendpoint(struct system_config *sc)
|
||||||
sc->sc_addr.ip = sc->sc_endpoint;
|
sc->sc_addr.ip = sc->sc_endpoint;
|
||||||
sc->sc_addr.family = 4;
|
sc->sc_addr.family = 4;
|
||||||
|
|
||||||
if (log_getverbose() > 2)
|
log_debug("%s: %s", __func__, ep);
|
||||||
log_debug("%s: %s", __func__, ep);
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
@ -1127,9 +1089,8 @@ usage(void)
|
||||||
{
|
{
|
||||||
extern char *__progname;
|
extern char *__progname;
|
||||||
|
|
||||||
fprintf(stderr, "usage: %s [-nuv] "
|
fprintf(stderr, "usage: %s [-nuv] [-t 3] [-U puffy] interface\n",
|
||||||
"[-c cloud[,...]] [-p length] [-r rootdisk]\n\t"
|
__progname);
|
||||||
"[-t 3] [-U puffy] interface\n", __progname);
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,165 +1124,25 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
cloud_add(enum cloudname name, struct clouds *clouds)
|
|
||||||
{
|
|
||||||
struct cloud *cloud;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(cloud, clouds, cloud_entry) {
|
|
||||||
if (cloud->cloud_name == name)
|
|
||||||
fatalx("cloud %s defined twice",
|
|
||||||
cloudnames[name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((cloud = calloc(1, sizeof(*cloud))) == NULL)
|
|
||||||
fatal("%s: calloc", __func__);
|
|
||||||
|
|
||||||
cloud->cloud_name = name;
|
|
||||||
switch (name) {
|
|
||||||
case AZURE:
|
|
||||||
cloud->fetch = azure;
|
|
||||||
break;
|
|
||||||
case CLOUDINIT:
|
|
||||||
cloud->fetch = cloudinit;
|
|
||||||
break;
|
|
||||||
case EC2:
|
|
||||||
cloud->fetch = ec2;
|
|
||||||
break;
|
|
||||||
case OPENNEBULA:
|
|
||||||
cloud->fetch = opennebula;
|
|
||||||
break;
|
|
||||||
case OPENSTACK:
|
|
||||||
cloud->fetch = openstack;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAILQ_INSERT_TAIL(clouds, cloud, cloud_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
trycloud(struct system_config *sc, struct cloud *cloud)
|
|
||||||
{
|
|
||||||
int errfd = -1, ret = -1;
|
|
||||||
|
|
||||||
free(sc->sc_endpoint);
|
|
||||||
sc->sc_endpoint = NULL;
|
|
||||||
|
|
||||||
switch (sc->sc_state) {
|
|
||||||
case STATE_INIT:
|
|
||||||
if ((cloud = TAILQ_FIRST(sc->sc_clouds)) == NULL)
|
|
||||||
return (0);
|
|
||||||
sc->sc_stack = cloudnames[cloud->cloud_name];
|
|
||||||
log_debug("%s: %s", __func__, sc->sc_stack);
|
|
||||||
TAILQ_REMOVE(sc->sc_clouds, cloud, cloud_entry);
|
|
||||||
break;
|
|
||||||
case STATE_DHCP:
|
|
||||||
sc->sc_state = STATE_169;
|
|
||||||
if (dhcp_getendpoint(sc) == -1)
|
|
||||||
return trycloud(sc, cloud);
|
|
||||||
break;
|
|
||||||
case STATE_169:
|
|
||||||
sc->sc_state = STATE_DONE;
|
|
||||||
if ((sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
|
|
||||||
log_warnx("failed to set defaults");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case STATE_DONE:
|
|
||||||
sc->sc_state = STATE_INIT;
|
|
||||||
ret = trycloud(sc, NULL);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
errfd = disable_output(sc, STDERR_FILENO);
|
|
||||||
ret = (*cloud->fetch)(sc);
|
|
||||||
enable_output(sc, STDERR_FILENO, errfd);
|
|
||||||
if (ret != 0)
|
|
||||||
return trycloud(sc, cloud);
|
|
||||||
|
|
||||||
done:
|
|
||||||
free(cloud);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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, sub;
|
int verbose = 0, dryrun = 0, unconfigure = 0;
|
||||||
int genpw = 0, ch, ret, timeout = CONNECT_TIMEOUT;
|
int ch, ret, timeout = CONNECT_TIMEOUT;
|
||||||
const char *error = NULL, *rootdisk = NULL;
|
const char *error = NULL;
|
||||||
char *args, *username = NULL, *options, *value;
|
char *args, *username = NULL;
|
||||||
struct clouds clouds;
|
|
||||||
|
|
||||||
/* log to stderr */
|
|
||||||
log_init(1, LOG_DAEMON);
|
|
||||||
|
|
||||||
if ((args = get_args(argc, argv)) == NULL)
|
if ((args = get_args(argc, argv)) == NULL)
|
||||||
fatalx("failed to save args");
|
fatalx("failed to save args");
|
||||||
|
|
||||||
TAILQ_INIT(&clouds);
|
while ((ch = getopt(argc, argv, "nvt:U:u")) != -1) {
|
||||||
|
|
||||||
while ((ch = getopt(argc, argv, "c:np:r:t:U:uv")) != -1) {
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'c':
|
|
||||||
options = optarg;
|
|
||||||
while (*options) {
|
|
||||||
if ((sub = getsubopt(&options,
|
|
||||||
cloudnames, &value)) == -1)
|
|
||||||
fatalx("invalid cloud stack");
|
|
||||||
else if (value != NULL)
|
|
||||||
fatalx("unexpected value");
|
|
||||||
cloud_add(sub, &clouds);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'n':
|
case 'n':
|
||||||
dryrun = 1;
|
dryrun = 1;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'v':
|
||||||
genpw = strtonum(optarg, 8, 8192, &error);
|
verbose += 2;
|
||||||
if (error != NULL)
|
|
||||||
fatalx("invalid password length: %s", error);
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
rootdisk = optarg;
|
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
timeout = strtonum(optarg, -1, 86400, &error);
|
timeout = strtonum(optarg, -1, 86400, &error);
|
||||||
|
@ -1335,9 +1156,6 @@ main(int argc, char *const *argv)
|
||||||
case 'u':
|
case 'u':
|
||||||
unconfigure = 1;
|
unconfigure = 1;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
|
||||||
verbose += 2;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
@ -1346,6 +1164,8 @@ main(int argc, char *const *argv)
|
||||||
argv += optind;
|
argv += optind;
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
|
|
||||||
|
/* log to stderr */
|
||||||
|
log_init(1, LOG_DAEMON);
|
||||||
log_setverbose(verbose);
|
log_setverbose(verbose);
|
||||||
|
|
||||||
if (unconfigure) {
|
if (unconfigure) {
|
||||||
|
@ -1356,62 +1176,29 @@ main(int argc, char *const *argv)
|
||||||
if (argc != 1)
|
if (argc != 1)
|
||||||
usage();
|
usage();
|
||||||
|
|
||||||
if (pledge(rootdisk == NULL ?
|
if (pledge("stdio cpath rpath wpath exec proc dns inet", NULL) == -1)
|
||||||
"stdio cpath rpath wpath exec proc dns inet" :
|
|
||||||
"stdio cpath rpath wpath exec proc dns inet disklabel",
|
|
||||||
NULL) == -1)
|
|
||||||
fatal("pledge");
|
fatal("pledge");
|
||||||
|
|
||||||
if ((sc = agent_init(argv[0], rootdisk, dryrun, timeout)) == NULL)
|
if ((sc = agent_init(argv[0], dryrun, timeout)) == NULL)
|
||||||
fatalx("agent");
|
fatalx("agent");
|
||||||
|
|
||||||
if (rootdisk != NULL && growdisk(sc) == -1)
|
|
||||||
fatalx("failed to grow %s", rootdisk);
|
|
||||||
|
|
||||||
sc->sc_clouds = &clouds;
|
|
||||||
sc->sc_args = args;
|
sc->sc_args = args;
|
||||||
if (username != NULL) {
|
if (username != NULL) {
|
||||||
free(sc->sc_username);
|
free(sc->sc_username);
|
||||||
sc->sc_username = username;
|
sc->sc_username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TAILQ_EMPTY(&clouds)) {
|
/*
|
||||||
/*
|
* XXX Detect cloud with help from hostctl and sysctl
|
||||||
* XXX Auto-detect cloud with help from hostctl and
|
* XXX in addition to the interface name.
|
||||||
* XXX sysctl in addition to the interface name.
|
*/
|
||||||
*/
|
if (opennebula(sc) == 0)
|
||||||
if (strcmp("hvn0", sc->sc_interface) == 0) {
|
ret = 0;
|
||||||
cloud_add(AZURE, &clouds);
|
else if (strcmp("hvn0", sc->sc_interface) == 0)
|
||||||
} else if (strcmp("xnf0", sc->sc_interface) == 0) {
|
ret = azure(sc);
|
||||||
cloud_add(OPENNEBULA, &clouds);
|
else if (strcmp("xnf0", sc->sc_interface) == 0)
|
||||||
cloud_add(EC2, &clouds);
|
ret = ec2(sc);
|
||||||
} else {
|
else
|
||||||
cloud_add(OPENNEBULA, &clouds);
|
ret = openstack(sc);
|
||||||
cloud_add(OPENSTACK, &clouds);
|
|
||||||
cloud_add(CLOUDINIT, &clouds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = trycloud(sc, NULL);
|
|
||||||
|
|
||||||
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 */
|
|
||||||
if (sc->sc_dryrun && sc->sc_userdata) {
|
|
||||||
if (agent_userdata(sc, sc->sc_userdata,
|
|
||||||
strlen(sc->sc_userdata)) != 0)
|
|
||||||
log_warnx("user-data failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sc->sc_stack != NULL)
|
if (sc->sc_stack != NULL)
|
||||||
log_debug("%s: %s", __func__, sc->sc_stack);
|
log_debug("%s: %s", __func__, sc->sc_stack);
|
||||||
|
|
37
agent/main.h
37
agent/main.h
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
* Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -29,30 +29,12 @@
|
||||||
#define DEFAULT_ENDPOINT "169.254.169.254"
|
#define DEFAULT_ENDPOINT "169.254.169.254"
|
||||||
#define CONNECT_TIMEOUT 10 /* in seconds */
|
#define CONNECT_TIMEOUT 10 /* in seconds */
|
||||||
|
|
||||||
enum cloudname {
|
|
||||||
AZURE,
|
|
||||||
CLOUDINIT,
|
|
||||||
EC2,
|
|
||||||
OPENNEBULA,
|
|
||||||
OPENSTACK
|
|
||||||
};
|
|
||||||
#define CLOUDNAMES { \
|
|
||||||
"azure", "cloudinit", "ec2", "opennebula", "openstack", NULL \
|
|
||||||
}
|
|
||||||
|
|
||||||
enum strtype {
|
enum strtype {
|
||||||
WORD,
|
WORD,
|
||||||
LINE,
|
LINE,
|
||||||
TEXT
|
TEXT
|
||||||
};
|
};
|
||||||
|
|
||||||
enum state {
|
|
||||||
STATE_INIT,
|
|
||||||
STATE_DHCP,
|
|
||||||
STATE_169,
|
|
||||||
STATE_DONE
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ssh_pubkey {
|
struct ssh_pubkey {
|
||||||
char *ssh_keyval;
|
char *ssh_keyval;
|
||||||
char *ssh_keyfp;
|
char *ssh_keyfp;
|
||||||
|
@ -84,34 +66,22 @@ struct net_addr {
|
||||||
};
|
};
|
||||||
TAILQ_HEAD(net_addrs, net_addr);
|
TAILQ_HEAD(net_addrs, net_addr);
|
||||||
|
|
||||||
struct system_config;
|
|
||||||
struct cloud {
|
|
||||||
enum cloudname cloud_name;
|
|
||||||
int (*fetch)(struct system_config *);
|
|
||||||
TAILQ_ENTRY(cloud) cloud_entry;
|
|
||||||
};
|
|
||||||
TAILQ_HEAD(clouds, cloud);
|
|
||||||
|
|
||||||
struct system_config {
|
struct system_config {
|
||||||
const char *sc_stack;
|
const char *sc_stack;
|
||||||
char *sc_args;
|
char *sc_args;
|
||||||
|
|
||||||
char *sc_hostname;
|
char *sc_hostname;
|
||||||
char *sc_username;
|
char *sc_username;
|
||||||
char *sc_password_plain;
|
char *sc_password;
|
||||||
char *sc_password_hash;
|
|
||||||
char *sc_pubkey;
|
char *sc_pubkey;
|
||||||
char *sc_userdata;
|
char *sc_userdata;
|
||||||
char *sc_endpoint;
|
char *sc_endpoint;
|
||||||
enum state sc_state;
|
|
||||||
struct clouds *sc_clouds;
|
|
||||||
char *sc_instance;
|
char *sc_instance;
|
||||||
int sc_timeout;
|
int sc_timeout;
|
||||||
|
|
||||||
const char *sc_ovfenv;
|
const char *sc_ovfenv;
|
||||||
const char *sc_interface;
|
const char *sc_interface;
|
||||||
const char *sc_cdrom;
|
const char *sc_cdrom;
|
||||||
const char *sc_rootdisk;
|
|
||||||
int sc_mount;
|
int sc_mount;
|
||||||
|
|
||||||
struct source sc_addr;
|
struct source sc_addr;
|
||||||
|
@ -159,9 +129,6 @@ int opennebula(struct system_config *);
|
||||||
/* openstack.c */
|
/* openstack.c */
|
||||||
int openstack(struct system_config *);
|
int openstack(struct system_config *);
|
||||||
|
|
||||||
/* growdisk.c */
|
|
||||||
int growdisk(struct system_config *);
|
|
||||||
|
|
||||||
/* main.c */
|
/* main.c */
|
||||||
int shell(const char *, ...);
|
int shell(const char *, ...);
|
||||||
int shellout(const char *, char **, const char *, ...);
|
int shellout(const char *, char **, const char *, ...);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
* Copyright (c) 2018 Reyk Floeter <reyk@openbsd.org>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -34,29 +34,25 @@ opennebula(struct system_config *sc)
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
const char *delim = "\\\\\0", *errstr;
|
const char *delim = "\\\\\0", *errstr;
|
||||||
char *line = NULL, *k, *v, *p, q;
|
char *line = NULL, *k, *v, *p, q;
|
||||||
char *value = NULL, *next = NULL, *last;
|
char *hname = NULL;
|
||||||
char *hname = NULL, *uname = NULL;
|
|
||||||
size_t len, lineno = 0, i;
|
size_t len, lineno = 0, i;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
unsigned short unit;
|
unsigned short unit;
|
||||||
|
|
||||||
if (sc->sc_state == STATE_INIT) {
|
|
||||||
sc->sc_state = STATE_169;
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return silently without error */
|
/* Return silently without error */
|
||||||
if ((fp = fopen("/mnt/context.sh", "r")) == NULL)
|
if ((fp = fopen("/mnt/context.sh", "r")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
sc->sc_stack = "opennebula";
|
||||||
|
|
||||||
while ((line = fparseln(fp, &len, &lineno,
|
while ((line = fparseln(fp, &len, &lineno,
|
||||||
delim, FPARSELN_UNESCALL)) != NULL) {
|
delim, FPARSELN_UNESCALL)) != NULL) {
|
||||||
/* key */
|
/* key */
|
||||||
k = line + strspn(line, " \t\r");
|
k = line;
|
||||||
|
|
||||||
/* a context always starts with this header */
|
/* a context always starts with this header */
|
||||||
if (lineno == 1) {
|
if (lineno == 1) {
|
||||||
ret = strcmp(k,
|
ret = strcmp(line,
|
||||||
"# Context variables generated by OpenNebula");
|
"# Context variables generated by OpenNebula");
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
log_debug("%s: unsupported context", __func__);
|
log_debug("%s: unsupported context", __func__);
|
||||||
|
@ -65,12 +61,7 @@ opennebula(struct system_config *sc)
|
||||||
free(line);
|
free(line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
line[strcspn(line, "#")] = '\0';
|
||||||
/* Strip comments that do not occur within a value */
|
|
||||||
if (*k == '#') {
|
|
||||||
free(line);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* value */
|
/* value */
|
||||||
if ((v = strchr(line, '=')) == NULL || *(v + 1) == '\0') {
|
if ((v = strchr(line, '=')) == NULL || *(v + 1) == '\0') {
|
||||||
|
@ -81,57 +72,20 @@ opennebula(struct system_config *sc)
|
||||||
|
|
||||||
/* value is quoted */
|
/* value is quoted */
|
||||||
q = *v;
|
q = *v;
|
||||||
if (strspn(v, "\"'") == 0) {
|
if (strspn(v, "\"'") == 0 || (p = strrchr(v, q)) == v) {
|
||||||
free(line);
|
free(line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
*v++ = '\0';
|
*v++ = '\0';
|
||||||
|
*p = '\0';
|
||||||
/* quoted value can be continued on multiple lines */
|
|
||||||
if ((value = strdup("")) == NULL) {
|
|
||||||
log_debug("%s: strdup", __func__);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
next = v;
|
|
||||||
do {
|
|
||||||
if ((p = strrchr(next, q)) != NULL)
|
|
||||||
*p++ = '\0';
|
|
||||||
if (*next) {
|
|
||||||
last = value;
|
|
||||||
if (asprintf(&value, "%s%s\n",
|
|
||||||
last, next) == -1) {
|
|
||||||
log_debug("%s: asprintf", __func__);
|
|
||||||
if (next != v)
|
|
||||||
free(next);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
free(last);
|
|
||||||
}
|
|
||||||
if (next != v)
|
|
||||||
free(next);
|
|
||||||
} while (p == NULL &&
|
|
||||||
(next = fparseln(fp, &len, &lineno,
|
|
||||||
delim, FPARSELN_UNESCALL)) != NULL);
|
|
||||||
next = NULL;
|
|
||||||
v = value;
|
|
||||||
|
|
||||||
/* strip trailing newline */
|
|
||||||
if ((p = strrchr(v, '\n')) != NULL)
|
|
||||||
*p = '\0';
|
|
||||||
|
|
||||||
/* continue if value is empty */
|
/* continue if value is empty */
|
||||||
if (*v == '\0') {
|
if (*v == '\0') {
|
||||||
free(line);
|
free(line);
|
||||||
free(value);
|
|
||||||
value = NULL;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print key/value unless it is a multi-line value */
|
log_debug("%s: %s = %s", __func__, k, v);
|
||||||
if (strcasecmp("SSH_PUBLIC_KEY", k) != 0 &&
|
|
||||||
strcasecmp("START_SCRIPT", k) != 0 &&
|
|
||||||
strcasecmp("START_SCRIPT_BASE64", k) != 0)
|
|
||||||
log_debug("%s: %s = %s", __func__, k, v);
|
|
||||||
|
|
||||||
if (strcasecmp("NETWORK", k) == 0) {
|
if (strcasecmp("NETWORK", k) == 0) {
|
||||||
if (strcasecmp("YES", v) == 0)
|
if (strcasecmp("YES", v) == 0)
|
||||||
|
@ -224,35 +178,11 @@ opennebula(struct system_config *sc)
|
||||||
if ((hname = strdup(v)) == NULL)
|
if ((hname = strdup(v)) == NULL)
|
||||||
log_warnx("failed to set hostname");
|
log_warnx("failed to set hostname");
|
||||||
} else if (strcasecmp("SSH_PUBLIC_KEY", k) == 0) {
|
} else if (strcasecmp("SSH_PUBLIC_KEY", k) == 0) {
|
||||||
do {
|
if (agent_addpubkey(sc, v, NULL) != 0)
|
||||||
p = v + strcspn(v, "\n");
|
log_warnx("failed to set ssh pubkey");
|
||||||
*p++ = '\0';
|
|
||||||
if (*v)
|
|
||||||
log_debug("%s: %s = %s",
|
|
||||||
__func__, k, v);
|
|
||||||
if (*v && agent_addpubkey(sc, v, NULL) != 0)
|
|
||||||
log_warnx("failed to set ssh pubkey");
|
|
||||||
v = p + strspn(p, "\n");
|
|
||||||
} while (*v != '\0');
|
|
||||||
} else if (strcasecmp("START_SCRIPT", k) == 0 ||
|
|
||||||
strcasecmp("START_SCRIPT_BASE64", k) == 0) {
|
|
||||||
log_debug("%s: %s = ...", __func__, k);
|
|
||||||
|
|
||||||
/* We will detect and decode base64 later */
|
|
||||||
if ((sc->sc_userdata = strdup(v)) == NULL)
|
|
||||||
log_warnx("failed to set userdata");
|
|
||||||
} else if (strcasecmp("USERNAME", k) == 0) {
|
|
||||||
if ((uname = strdup(v)) == NULL)
|
|
||||||
log_warnx("failed to set username");
|
|
||||||
else {
|
|
||||||
free(sc->sc_username);
|
|
||||||
sc->sc_username = uname;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
free(value);
|
|
||||||
value = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
@ -286,6 +216,5 @@ opennebula(struct system_config *sc)
|
||||||
if (fp != NULL)
|
if (fp != NULL)
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
free(line);
|
free(line);
|
||||||
free(value);
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, 2019 Reyk Floeter <reyk@openbsd.org>
|
* Copyright (c) 2018 Reyk Floeter <reyk@openbsd.org>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -34,11 +34,18 @@ static int openstack_fetch(struct system_config *);
|
||||||
int
|
int
|
||||||
openstack(struct system_config *sc)
|
openstack(struct system_config *sc)
|
||||||
{
|
{
|
||||||
if (sc->sc_state == STATE_INIT) {
|
if ((dhcp_getendpoint(sc) == -1) &&
|
||||||
sc->sc_state = STATE_DHCP;
|
(sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
|
||||||
|
log_warnx("failed to set defaults");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
return openstack_fetch(sc);
|
|
||||||
|
if (openstack_fetch(sc) != 0) {
|
||||||
|
free(sc->sc_endpoint);
|
||||||
|
return (cloudinit(sc));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -56,6 +63,7 @@ openstack_fetch(struct system_config *sc)
|
||||||
if ((json = metadata(sc,
|
if ((json = metadata(sc,
|
||||||
"/openstack/latest/meta_data.json", TEXT)) == NULL)
|
"/openstack/latest/meta_data.json", TEXT)) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
sc->sc_stack = "openstack";
|
||||||
|
|
||||||
if ((j = json_parse(json, strlen(json))) == NULL)
|
if ((j = json_parse(json, strlen(json))) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
150
cloud-agent.md
150
cloud-agent.md
|
@ -1,150 +0,0 @@
|
||||||
CLOUD-AGENT(8) - System Manager's Manual
|
|
||||||
|
|
||||||
# NAME
|
|
||||||
|
|
||||||
**cloud-agent** - cloud provisioning for OpenBSD VMs
|
|
||||||
|
|
||||||
# SYNOPSIS
|
|
||||||
|
|
||||||
**cloud-agent**
|
|
||||||
\[**-nuv**]
|
|
||||||
\[**-c** *cloud*\[,*cloud*...]]
|
|
||||||
\[**-p** *length*]
|
|
||||||
\[**-r** *rootdisk*]
|
|
||||||
\[**-t** *timeout*]
|
|
||||||
\[**-U** *username*]
|
|
||||||
*interface*
|
|
||||||
|
|
||||||
# DESCRIPTION
|
|
||||||
|
|
||||||
The
|
|
||||||
**cloud-agent**
|
|
||||||
program manages the OpenBSD provisioning and VM interaction in cloud
|
|
||||||
environments.
|
|
||||||
|
|
||||||
The options are as follows:
|
|
||||||
|
|
||||||
**-c** *cloud*\[,*cloud*...]
|
|
||||||
|
|
||||||
> Probe a list of cloud stacks for provisioning in the specified order.
|
|
||||||
> If this option is not specified,
|
|
||||||
> **cloud-agent**
|
|
||||||
> tries to detect the environment and possible cloud stacks automatically.
|
|
||||||
> Supported
|
|
||||||
> *cloud*
|
|
||||||
> stacks are:
|
|
||||||
|
|
||||||
> **azure**
|
|
||||||
|
|
||||||
> > Microsoft Azure
|
|
||||||
|
|
||||||
> **cloudinit**
|
|
||||||
|
|
||||||
> > Generic cloud-init
|
|
||||||
|
|
||||||
> **ec2**
|
|
||||||
|
|
||||||
> > Amazon AWS EC2
|
|
||||||
|
|
||||||
> **opennebula**
|
|
||||||
|
|
||||||
> > OpenNebula
|
|
||||||
|
|
||||||
> **openstack**
|
|
||||||
|
|
||||||
> > OpenStack
|
|
||||||
|
|
||||||
**-p** *length*
|
|
||||||
|
|
||||||
> Generate and set a random password for the default user.
|
|
||||||
> The password will be written in its plain form into the
|
|
||||||
> *~/.ssh/authorized\_keys*
|
|
||||||
> file.
|
|
||||||
> This allows to use the
|
|
||||||
> doas(1)
|
|
||||||
> command to gain root privileges.
|
|
||||||
> The minimum
|
|
||||||
> *length*
|
|
||||||
> is 8 characters and the default is an empty password.
|
|
||||||
|
|
||||||
**-n**
|
|
||||||
|
|
||||||
> Do not configure the system and skip the provisioning step.
|
|
||||||
|
|
||||||
**-t** *timeout*
|
|
||||||
|
|
||||||
> Change the HTTP timeout.
|
|
||||||
> The default is 3 seconds.
|
|
||||||
|
|
||||||
**-U** *username*
|
|
||||||
|
|
||||||
> Change the default user.
|
|
||||||
> The default is
|
|
||||||
> "ec2-user"
|
|
||||||
> on AWS,
|
|
||||||
> "azure-user"
|
|
||||||
> on Azure, and
|
|
||||||
> "puffy"
|
|
||||||
> everywhere else.
|
|
||||||
> The default user is used when it is not obtained from the cloud
|
|
||||||
> configuration.
|
|
||||||
> Using
|
|
||||||
> "root"
|
|
||||||
> is supported, but not recommended.
|
|
||||||
|
|
||||||
**-r** *rootdisk*
|
|
||||||
|
|
||||||
> Automatically grow the last
|
|
||||||
> OpenBSD
|
|
||||||
> FFS partition of the root disk to use all the available space.
|
|
||||||
|
|
||||||
**-u**
|
|
||||||
|
|
||||||
> Deprovision and unconfigure the system.
|
|
||||||
> This deletes keys, passwords, and logs files without asking for permission.
|
|
||||||
|
|
||||||
**-v**
|
|
||||||
|
|
||||||
> Produce more verbose output.
|
|
||||||
|
|
||||||
Enable
|
|
||||||
**cloud-agent**
|
|
||||||
in the
|
|
||||||
hostname.if(5)
|
|
||||||
of the VM's primary networking interface and automatically the last
|
|
||||||
partition of the root disk:
|
|
||||||
|
|
||||||
# cat /etc/hostname.hvn0
|
|
||||||
dhcp
|
|
||||||
!/usr/local/libexec/cloud-agent -r sd0 "\$if"
|
|
||||||
|
|
||||||
# FILES
|
|
||||||
|
|
||||||
*~/.ssh/authorized\_keys*
|
|
||||||
|
|
||||||
> The location of the agent-configured SSH public keys and optional password.
|
|
||||||
|
|
||||||
*/usr/local/libexec/cloud-agent*
|
|
||||||
|
|
||||||
> The agent itself.
|
|
||||||
|
|
||||||
*/usr/local/bin/cms*
|
|
||||||
|
|
||||||
> The CMS binary that is used to decrypt messages from the Azure fabric.
|
|
||||||
|
|
||||||
*/var/db/cloud-instance*
|
|
||||||
|
|
||||||
> The instance ID as reported by the cloud.
|
|
||||||
> **cloud-agent**
|
|
||||||
> reprovisions the system when the value has changed.
|
|
||||||
|
|
||||||
# SEE ALSO
|
|
||||||
|
|
||||||
meta-data(8),
|
|
||||||
vmd(8)
|
|
||||||
|
|
||||||
# AUTHORS
|
|
||||||
|
|
||||||
Reyk Floeter <[reyk@openbsd.org](mailto:reyk@openbsd.org)>
|
|
||||||
|
|
||||||
OpenBSD 6.5 - June 26, 2019
|
|
Loading…
Reference in a new issue