Compare commits
49 Commits
Author | SHA1 | Date |
---|---|---|
reykfloeter | 14d0149cdb | |
reykfloeter | beef9f736c | |
reykfloeter | 0b1fee287c | |
reykfloeter | c4595717fb | |
reykfloeter | e800a2b7d2 | |
reykfloeter | 39f33c76a4 | |
reykfloeter | fd7fa10f1b | |
reykfloeter | c968f169fd | |
reykfloeter | 678b29acef | |
reykfloeter | ca22cba8e4 | |
reykfloeter | ba34eb76dd | |
reykfloeter | 3be9707418 | |
reykfloeter | b8ae4a13fc | |
reykfloeter | c5c1705cd7 | |
reykfloeter | 99f8b2d2b0 | |
reykfloeter | 0509d8d619 | |
reykfloeter | bce8634bf5 | |
reykfloeter | 95e8cb1cb1 | |
reykfloeter | 63935a1b5f | |
reykfloeter | 139f35d9d4 | |
reykfloeter | aa963100ba | |
reykfloeter | 8dfa3c843a | |
reykfloeter | a8490a757f | |
reykfloeter | cf0be19caa | |
reykfloeter | ffe93c5b4f | |
reykfloeter | 3e3c5d914e | |
reykfloeter | 3290c27210 | |
reykfloeter | 20e2f78f83 | |
reykfloeter | 91eb82f902 | |
reykfloeter | 333f7ac6d7 | |
reykfloeter | d9899d488a | |
reykfloeter | ec87db177d | |
reykfloeter | bc8d60d5f6 | |
reykfloeter | daec249cc0 | |
reykfloeter | bf8bfd607b | |
reykfloeter | 8c4f6a384b | |
reykfloeter | 5de42d6464 | |
Reyk Floeter | f48b2bc2b9 | |
Reyk Floeter | 155d216845 | |
Reyk Floeter | 73f066699f | |
Reyk Floeter | cb7edf897e | |
Reyk Floeter | 4953fa418c | |
Reyk Floeter | 9ffe04f62f | |
Reyk Floeter | eaa8b96541 | |
Reyk Floeter | ee473a4bd6 | |
Reyk Floeter | ac66f160e0 | |
Reyk Floeter | eb9d5b440c | |
Reyk Floeter | cdf3317965 | |
Reyk Floeter | 63d46cd6f1 |
|
@ -0,0 +1,69 @@
|
|||
# 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.
|
||||
* Most of the code has been written by Reyk Floeter <reyk@openbsd.org>
|
||||
* The http.[ch] files have been written by Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
* The {http,json}.[ch] files were written by Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
* Please refer to the individual source files for other copyright holders!
|
||||
|
||||
> Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||
> Copyright (c) 2017, 2018, 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
|
||||
|
@ -26,5 +26,5 @@ License
|
|||
`cms/`
|
||||
------
|
||||
|
||||
* The CMS code is from the OpenSSL and/or LibreSSL.
|
||||
* The CMS code is from OpenSSL and/or LibreSSL.
|
||||
* Please refer to the individual source files for other copyright holders!
|
||||
|
|
5
Makefile
5
Makefile
|
@ -1,10 +1,13 @@
|
|||
#
|
||||
# The Azure agents needs CMS to obtain the SSH public keys.
|
||||
# LibreSSL has removed CMS, so either use OpenSSL to decrypt CMS
|
||||
# messages or compile the old CMS code for LibreSSL.
|
||||
# messages or compile the old CMS code for LibreSSL. Or use
|
||||
# CMS that has returned to newer versions of LibreSSL.
|
||||
#
|
||||
.ifdef USE_OPENSSL
|
||||
MAKE_FLAGS+= USE_OPENSSL=1
|
||||
.elifdef USE_LIBRESSL_CMS
|
||||
MAKE_FLAGS+= USE_LIBRESSL_CMS=1
|
||||
.else
|
||||
SUBDIR= cms
|
||||
.endif
|
||||
|
|
27
README.md
27
README.md
|
@ -1,8 +1,6 @@
|
|||
cloud-agent for OpenBSD
|
||||
=======================
|
||||
|
||||
**This is just experimental. Be warned.**
|
||||
|
||||
This is a simple OpenBSD-specific agent that aims to handle
|
||||
provisioning and cloud initialization on public clouds such as
|
||||
Microsoft Azure and Amazon AWS. For OpenBSD on Azure, it is a minimal
|
||||
|
@ -25,19 +23,40 @@ has removed CMS which is required by Azure.
|
|||
Usage
|
||||
-----
|
||||
|
||||
Installation is easy, `cloud-agent` detects the cloud type automatically.
|
||||
See the [cloud-agent(8)](cloud-agent.md) documentation for more
|
||||
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 Amazon AWS, create a file `/etc/hostname.xnf0`
|
||||
|
||||
* On CloudStack, such as Exoscale, create a file `/etc/hostname.vio0`
|
||||
|
||||
* On OpenBSD VMM (with meta-data), create a file `/etc/hostname.vio0`
|
||||
|
||||
* The content of the file is identical for all of them:
|
||||
* On OpenStack/VMware, create a file `/etc/hostname.vmx0`
|
||||
|
||||
* The content of the file is identical for all of the above:
|
||||
|
||||
dhcp
|
||||
!/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
|
||||
------
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
PROG= cloud-agent
|
||||
SRCS= main.c xml.c azure.c cloudinit.c http.c log.c
|
||||
SRCS= http.c json.c jsmn.c log.c main.c xml.c
|
||||
SRCS+= azure.c cloudinit.c opennebula.c openstack.c
|
||||
SRCS+= growdisk.c
|
||||
BINDIR= /usr/local/libexec
|
||||
MANDIR= /usr/local/man/man
|
||||
|
||||
|
@ -15,7 +17,7 @@ CFLAGS+= -Wmissing-declarations
|
|||
CFLAGS+= -Wshadow -Wpointer-arith
|
||||
CFLAGS+= -Wsign-compare -Wcast-qual
|
||||
|
||||
LDADD+= -lexpat -ltls -lssl -lcrypto
|
||||
DPADD+= ${LIBEXPAT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO}
|
||||
LDADD+= -lexpat -ltls -lssl -lcrypto -lutil
|
||||
DPADD+= ${LIBEXPAT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} ${LIBUTIL}
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
|
158
agent/azure.c
158
agent/azure.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||
* Copyright (c) 2017, 2018, 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
|
||||
|
@ -23,8 +23,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "http.h"
|
||||
#include "xml.h"
|
||||
|
@ -51,7 +54,6 @@ static struct httpget
|
|||
|
||||
static int azure_keys(struct system_config *);
|
||||
static int azure_getpubkeys(struct system_config *);
|
||||
static int azure_getendpoint(struct system_config *);
|
||||
static int azure_getovfenv(struct system_config *);
|
||||
static int azure_versions(struct system_config *);
|
||||
static int azure_goalstate(struct system_config *);
|
||||
|
@ -61,54 +63,64 @@ static int azure_reporthealth(struct system_config *, const char *);
|
|||
int
|
||||
azure(struct system_config *sc)
|
||||
{
|
||||
int ret = -1;
|
||||
int ret = -1;
|
||||
|
||||
/* Apply defaults */
|
||||
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
||||
log_warnx("failed to set default user");
|
||||
goto done;
|
||||
}
|
||||
sc->sc_cdrom = "/dev/cd0c";
|
||||
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
||||
sc->sc_priv = &az_config;
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
free(sc->sc_username);
|
||||
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
||||
log_warnx("failed to set default user");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (azure_getendpoint(sc) != 0) {
|
||||
log_warnx("failed to get endpoint");
|
||||
goto done;
|
||||
/* Apply defaults */
|
||||
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
||||
sc->sc_priv = &az_config;
|
||||
sc->sc_state = STATE_DHCP;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Don't try other endpoints */
|
||||
sc->sc_state = STATE_DONE;
|
||||
|
||||
if (azure_getovfenv(sc) != 0) {
|
||||
log_warnx("failed to get ovf-env.xml");
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (dhcp_getendpoint(sc) != 0) {
|
||||
log_warnx("failed to get endpoint");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (azure_versions(sc) != 0) {
|
||||
log_warnx("failed to get endpoint versions");
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (azure_goalstate(sc) != 0) {
|
||||
log_warnx("failed to get goalstate");
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (azure_keys(sc) != 0) {
|
||||
log_warnx("failed to get transport keys");
|
||||
goto done;
|
||||
}
|
||||
if (!sc->sc_dryrun) {
|
||||
if (azure_keys(sc) != 0) {
|
||||
log_warnx("failed to get transport keys");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (azure_certificates(sc) != 0) {
|
||||
log_warnx("failed to get certificates");
|
||||
goto done;
|
||||
if (azure_certificates(sc) != 0) {
|
||||
log_warnx("failed to get certificates");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (azure_reporthealth(sc, "Ready") != 0) {
|
||||
log_warnx("failed to report health");
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
done:
|
||||
fail:
|
||||
free(az_config.az_container);
|
||||
free(az_config.az_pubkeyval);
|
||||
|
||||
|
@ -431,13 +443,16 @@ azure_certificates(struct system_config *sc)
|
|||
|
||||
fd = disable_output(sc, STDERR_FILENO);
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#if defined(USE_OPENSSL)
|
||||
/*
|
||||
* XXX Now comes the part that needs CMS which is only
|
||||
* XXX present in OpenSSL but got removed from LibreSSL.
|
||||
*/
|
||||
log_debug("%s: running openssl cms", __func__);
|
||||
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
|
||||
if (shell("/usr/local/bin/cms",
|
||||
#endif
|
||||
|
@ -652,36 +667,22 @@ azure_getovfenv(struct system_config *sc)
|
|||
struct xml xml;
|
||||
struct xmlelem *xp, *xe, *xk, *xv;
|
||||
char *sshfp, *sshval, *str;
|
||||
int mount = 0, ret = -1, fd = -1;
|
||||
int ret = -1, fd = -1;
|
||||
FILE *fp;
|
||||
|
||||
/* Silently try to mount the cdrom */
|
||||
fd = disable_output(sc, STDERR_FILENO);
|
||||
ret = shell("mount", "-r", sc->sc_cdrom, "/mnt", NULL);
|
||||
enable_output(sc, STDERR_FILENO, fd);
|
||||
fd = -1;
|
||||
|
||||
if (ret == 0) {
|
||||
log_debug("%s: mounted %s", __func__, sc->sc_cdrom);
|
||||
mount = 1;
|
||||
}
|
||||
ret = -1;
|
||||
|
||||
if (xml_init(&xml) != 0) {
|
||||
log_debug("%s: xml", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Fallback to and older ovf-env.xml file */
|
||||
/*
|
||||
* Assume that the cdrom is already mounted.
|
||||
* Fallback to and older ovf-env.xml file.
|
||||
*/
|
||||
if (xml_parse(&xml, "/mnt/ovf-env.xml") == -1 &&
|
||||
xml_parse(&xml, sc->sc_ovfenv) == -1)
|
||||
goto done;
|
||||
|
||||
/* unmount if we mounted the cdrom before */
|
||||
if (mount && shell("umount", "/mnt", NULL) == 0) {
|
||||
log_debug("%s: unmounted %s", __func__, sc->sc_cdrom);
|
||||
}
|
||||
|
||||
if ((xp = xml_findl(&xml.ox_root,
|
||||
"Environment", "wa:ProvisioningSection",
|
||||
"LinuxProvisioningConfigurationSet", NULL)) == NULL) {
|
||||
|
@ -730,7 +731,7 @@ azure_getovfenv(struct system_config *sc)
|
|||
}
|
||||
|
||||
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__);
|
||||
goto done;
|
||||
}
|
||||
|
@ -738,13 +739,14 @@ azure_getovfenv(struct system_config *sc)
|
|||
str = strndup(xe->xe_data, xe->xe_datalen);
|
||||
if (str == NULL ||
|
||||
crypt_newhash(str, "bcrypt,a",
|
||||
sc->sc_password, 128) != 0) {
|
||||
sc->sc_password_hash, _PASSWORD_LEN) != 0) {
|
||||
log_debug("%s: password hashing failed", __func__);
|
||||
free(sc->sc_password);
|
||||
sc->sc_password = NULL;
|
||||
free(sc->sc_password_hash);
|
||||
sc->sc_password_hash = NULL;
|
||||
free(str);
|
||||
goto done;
|
||||
}
|
||||
explicit_bzero(str, xe->xe_datalen);
|
||||
free(str);
|
||||
|
||||
/* Replace unencrypted password with hash */
|
||||
|
@ -754,11 +756,11 @@ azure_getovfenv(struct system_config *sc)
|
|||
/* Update element for xml_print() below */
|
||||
explicit_bzero(xe->xe_data, xe->xe_datalen);
|
||||
free(xe->xe_data);
|
||||
xe->xe_data = strdup(sc->sc_password);
|
||||
xe->xe_datalen = strlen(sc->sc_password);
|
||||
xe->xe_data = strdup(sc->sc_password_hash);
|
||||
xe->xe_datalen = strlen(sc->sc_password_hash);
|
||||
} else if ((xe = xml_findl(&xp->xe_head,
|
||||
"UserPasswordHash", NULL)) != NULL) {
|
||||
if ((sc->sc_password =
|
||||
if ((sc->sc_password_hash =
|
||||
get_word(xe->xe_data, xe->xe_datalen)) != NULL) {
|
||||
log_debug("%s: password hash failed", __func__);
|
||||
goto done;
|
||||
|
@ -791,53 +793,3 @@ azure_getovfenv(struct system_config *sc)
|
|||
xml_free(&xml);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
azure_getendpoint(struct system_config *sc)
|
||||
{
|
||||
char path[PATH_MAX], buf[BUFSIZ], *ep = NULL;
|
||||
int a[4];
|
||||
FILE *fp;
|
||||
|
||||
if ((size_t)snprintf(path, sizeof(path), "/var/db/dhclient.leases.%s",
|
||||
sc->sc_interface) >= sizeof(path)) {
|
||||
log_debug("%s: invalid path", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((fp = fopen(path, "r")) == NULL) {
|
||||
log_debug("%s: failed to open %s", __func__, path);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
buf[strcspn(buf, ";\n")] = '\0';
|
||||
|
||||
/* Find last occurence of option-245 */
|
||||
if (sscanf(buf, " option option-245 %x:%x:%x:%x",
|
||||
&a[0], &a[1], &a[2], &a[3]) == 4) {
|
||||
free(ep);
|
||||
if (asprintf(&ep, "%d.%d.%d.%d",
|
||||
a[0], a[1], a[2], a[3]) == -1) {
|
||||
log_debug("%s: asprintf", __func__);
|
||||
fclose(fp);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (ep == NULL) {
|
||||
log_debug("%s: endpoint not found", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
sc->sc_endpoint = ep;
|
||||
sc->sc_addr.ip = sc->sc_endpoint;
|
||||
sc->sc_addr.family = 4;
|
||||
|
||||
log_debug("%s: %s", __func__, ep);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" $OpenBSD: mdoc.template,v 1.15 2014/03/31 00:09:54 dlg Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||
.\" Copyright (c) 2017, 2018, 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
|
||||
|
@ -23,17 +23,75 @@
|
|||
.Sh SYNOPSIS
|
||||
.Nm cloud-agent
|
||||
.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 U Ar username
|
||||
.Ar interface
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program manages the OpenBSD provisioning and VM interaction in cloud
|
||||
environments, including Microsoft Azure and Amazon AWS.
|
||||
environments.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.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
|
||||
Do not configure the system and skip the provisioning step.
|
||||
.It Fl t Ar timeout
|
||||
Change the HTTP timeout.
|
||||
The default is 3 seconds.
|
||||
.It Fl U Ar username
|
||||
Change the default user.
|
||||
The default is
|
||||
.Dq ec2-user
|
||||
on AWS,
|
||||
.Dq azure-user
|
||||
on Azure, and
|
||||
.Dq puffy
|
||||
everywhere else.
|
||||
The default user is used when it is not obtained from the cloud
|
||||
configuration.
|
||||
Using
|
||||
.Dq root
|
||||
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
|
||||
Deprovision and unconfigure the system.
|
||||
This deletes keys, passwords, and logs files without asking for permission.
|
||||
|
@ -45,14 +103,17 @@ Enable
|
|||
.Nm
|
||||
in the
|
||||
.Xr hostname.if 5
|
||||
of the VM's primary networking interface:
|
||||
of the VM's primary networking interface and automatically the last
|
||||
partition of the root disk:
|
||||
.Bd -literal -offset indent
|
||||
# cat /etc/hostname.hvn0
|
||||
dhcp
|
||||
!/usr/local/libexec/cloud-agent "\$if"
|
||||
!/usr/local/libexec/cloud-agent -r sd0 "\e$if"
|
||||
.Ed
|
||||
.Sh FILES
|
||||
.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
|
||||
The agent itself.
|
||||
.It Pa /usr/local/bin/cms
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||
* Copyright (c) 2017, 2018, 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
|
||||
|
@ -30,101 +30,69 @@
|
|||
#include "xml.h"
|
||||
|
||||
static int cloudinit_fetch(struct system_config *);
|
||||
static char *cloudinit_get(struct system_config *, const char *,
|
||||
enum strtype);
|
||||
|
||||
int
|
||||
ec2(struct system_config *sc)
|
||||
{
|
||||
if ((sc->sc_username = strdup("ec2-user")) == NULL ||
|
||||
(sc->sc_endpoint = strdup("169.254.169.254")) == NULL) {
|
||||
log_warnx("failed to set defaults");
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
free(sc->sc_username);
|
||||
if ((sc->sc_username = strdup("ec2-user")) == NULL) {
|
||||
log_warnx("failed to set default user");
|
||||
return (-1);
|
||||
}
|
||||
sc->sc_state = STATE_169;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (cloudinit_fetch(sc));
|
||||
return cloudinit_fetch(sc);
|
||||
}
|
||||
|
||||
int
|
||||
cloudinit(struct system_config *sc)
|
||||
{
|
||||
/* XXX get endpoint from DHCP lease file */
|
||||
if ((sc->sc_username = strdup("puffy")) == NULL ||
|
||||
(sc->sc_endpoint = strdup("169.254.169.254")) == NULL) {
|
||||
log_warnx("failed to set defaults");
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
sc->sc_state = STATE_DHCP;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (cloudinit_fetch(sc));
|
||||
}
|
||||
|
||||
static char *
|
||||
cloudinit_get(struct system_config *sc, const char *path, enum strtype type)
|
||||
{
|
||||
struct httpget *g = NULL;
|
||||
char *str = NULL;
|
||||
|
||||
log_debug("%s: %s", __func__, path);
|
||||
|
||||
g = http_get(&sc->sc_addr, 1,
|
||||
sc->sc_endpoint, 80, path, NULL, 0, NULL);
|
||||
if (g != NULL && g->code == 200 && g->bodypartsz > 0) {
|
||||
switch (type) {
|
||||
case TEXT:
|
||||
/* multi-line string, always printable */
|
||||
str = get_string(g->bodypart, g->bodypartsz);
|
||||
break;
|
||||
case LINE:
|
||||
str = get_line(g->bodypart, g->bodypartsz);
|
||||
break;
|
||||
case WORD:
|
||||
str = get_word(g->bodypart, g->bodypartsz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
http_get_free(g);
|
||||
|
||||
return (str);
|
||||
return cloudinit_fetch(sc);
|
||||
}
|
||||
|
||||
static int
|
||||
cloudinit_fetch(struct system_config *sc)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret = -1;
|
||||
char *str = NULL;
|
||||
|
||||
sc->sc_addr.ip = sc->sc_endpoint;
|
||||
sc->sc_addr.family = 4;
|
||||
|
||||
/* instance-id */
|
||||
if ((sc->sc_instance = cloudinit_get(sc,
|
||||
if ((sc->sc_instance = metadata(sc,
|
||||
"/latest/meta-data/instance-id", WORD)) == NULL)
|
||||
goto fail;
|
||||
|
||||
/* hostname */
|
||||
if ((sc->sc_hostname = cloudinit_get(sc,
|
||||
if ((sc->sc_hostname = metadata(sc,
|
||||
"/latest/meta-data/local-hostname", WORD)) == NULL)
|
||||
goto fail;
|
||||
|
||||
/* pubkey */
|
||||
if ((str = cloudinit_get(sc,
|
||||
"/latest/meta-data/public-keys/0/openssh-key", LINE)) == NULL)
|
||||
goto fail;
|
||||
if (agent_addpubkey(sc, str, NULL) != 0)
|
||||
/* optional pubkey */
|
||||
if ((str = metadata(sc,
|
||||
"/latest/meta-data/public-keys/0/openssh-key", LINE)) == NULL &&
|
||||
(str = metadata(sc,
|
||||
"/latest/meta-data/public-keys", LINE)) == NULL)
|
||||
log_warnx("failed to get public key");
|
||||
else if (agent_addpubkey(sc, str, NULL) != 0)
|
||||
goto fail;
|
||||
|
||||
/* optional username - this is an extension by meta-data(8) */
|
||||
if ((str = cloudinit_get(sc,
|
||||
"/latest/meta-data/username", WORD)) != NULL) {
|
||||
if ((str = metadata(sc, "/latest/meta-data/username", WORD)) != NULL) {
|
||||
free(sc->sc_username);
|
||||
sc->sc_username = str;
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
/* userdata */
|
||||
if ((sc->sc_userdata = cloudinit_get(sc,
|
||||
"/latest/user-data", TEXT)) == NULL)
|
||||
goto fail;
|
||||
/* userdata (optional) */
|
||||
sc->sc_userdata = metadata(sc, "/latest/user-data", TEXT);
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -277,8 +277,8 @@ again:
|
|||
if (fd == -1) {
|
||||
warn("%s: socket", addrs[cur].ip);
|
||||
goto again;
|
||||
} else if (connect(fd, (struct sockaddr *)&ss, len) == -1) {
|
||||
warn("%s: connect", addrs[cur].ip);
|
||||
} else if (connect_wait(fd, (struct sockaddr *)&ss, len) == -1) {
|
||||
warn("http://%s%s", addrs[cur].ip, path);
|
||||
close(fd);
|
||||
goto again;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
Copyright (c) 2010 Serge A. Zaitsev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.*
|
||||
*/
|
||||
#include "jsmn.h"
|
||||
|
||||
/**
|
||||
* Allocates a fresh unused token from the token pull.
|
||||
*/
|
||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
|
||||
jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *tok;
|
||||
if (parser->toknext >= num_tokens) {
|
||||
return NULL;
|
||||
}
|
||||
tok = &tokens[parser->toknext++];
|
||||
tok->start = tok->end = -1;
|
||||
tok->size = 0;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
tok->parent = -1;
|
||||
#endif
|
||||
return tok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills token type and boundaries.
|
||||
*/
|
||||
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
|
||||
int start, int end) {
|
||||
token->type = type;
|
||||
token->start = start;
|
||||
token->end = end;
|
||||
token->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next available token with JSON primitive.
|
||||
*/
|
||||
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
||||
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
int start;
|
||||
|
||||
start = parser->pos;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
switch (js[parser->pos]) {
|
||||
#ifndef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
||||
case ':':
|
||||
#endif
|
||||
case '\t' : case '\r' : case '\n' : case ' ' :
|
||||
case ',' : case ']' : case '}' :
|
||||
goto found;
|
||||
}
|
||||
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by a comma/object/array */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
#endif
|
||||
|
||||
found:
|
||||
if (tokens == NULL) {
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next token with JSON string.
|
||||
*/
|
||||
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
|
||||
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
|
||||
int start = parser->pos;
|
||||
|
||||
parser->pos++;
|
||||
|
||||
/* Skip starting quote */
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
|
||||
/* Quote: end of string */
|
||||
if (c == '\"') {
|
||||
if (tokens == NULL) {
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backslash: Quoted symbol expected */
|
||||
if (c == '\\' && parser->pos + 1 < len) {
|
||||
int i;
|
||||
parser->pos++;
|
||||
switch (js[parser->pos]) {
|
||||
/* Allowed escaped symbols */
|
||||
case '\"': case '/' : case '\\' : case 'b' :
|
||||
case 'f' : case 'r' : case 'n' : case 't' :
|
||||
break;
|
||||
/* Allows escaped symbol \uXXXX */
|
||||
case 'u':
|
||||
parser->pos++;
|
||||
for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
|
||||
/* If it isn't a hex character we have an error */
|
||||
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
|
||||
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
|
||||
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->pos++;
|
||||
}
|
||||
parser->pos--;
|
||||
break;
|
||||
/* Unexpected symbol */
|
||||
default:
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON string and fill tokens.
|
||||
*/
|
||||
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
||||
jsmntok_t *tokens, unsigned int num_tokens) {
|
||||
int r;
|
||||
int i;
|
||||
jsmntok_t *token;
|
||||
int count = parser->toknext;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c;
|
||||
jsmntype_t type;
|
||||
|
||||
c = js[parser->pos];
|
||||
switch (c) {
|
||||
case '{': case '[':
|
||||
count++;
|
||||
if (tokens == NULL) {
|
||||
break;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL)
|
||||
return JSMN_ERROR_NOMEM;
|
||||
if (parser->toksuper != -1) {
|
||||
tokens[parser->toksuper].size++;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
}
|
||||
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
token->start = parser->pos;
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case '}': case ']':
|
||||
if (tokens == NULL)
|
||||
break;
|
||||
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
if (parser->toknext < 1) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token = &tokens[parser->toknext - 1];
|
||||
for (;;) {
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token->end = parser->pos + 1;
|
||||
parser->toksuper = token->parent;
|
||||
break;
|
||||
}
|
||||
if (token->parent == -1) {
|
||||
break;
|
||||
}
|
||||
token = &tokens[token->parent];
|
||||
}
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->toksuper = -1;
|
||||
token->end = parser->pos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Error if unmatched closing bracket */
|
||||
if (i == -1) return JSMN_ERROR_INVAL;
|
||||
for (; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case '\"':
|
||||
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
case '\t' : case '\r' : case '\n' : case ' ':
|
||||
break;
|
||||
case ':':
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case ',':
|
||||
if (tokens != NULL && parser->toksuper != -1 &&
|
||||
tokens[parser->toksuper].type != JSMN_ARRAY &&
|
||||
tokens[parser->toksuper].type != JSMN_OBJECT) {
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
parser->toksuper = tokens[parser->toksuper].parent;
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitives are: numbers and booleans */
|
||||
case '-': case '0': case '1' : case '2': case '3' : case '4':
|
||||
case '5': case '6': case '7' : case '8': case '9':
|
||||
case 't': case 'f': case 'n' :
|
||||
/* And they must not be keys of the object */
|
||||
if (tokens != NULL && parser->toksuper != -1) {
|
||||
jsmntok_t *t = &tokens[parser->toksuper];
|
||||
if (t->type == JSMN_OBJECT ||
|
||||
(t->type == JSMN_STRING && t->size != 0)) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* In non-strict mode every unquoted value is a primitive */
|
||||
default:
|
||||
#endif
|
||||
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
|
||||
#ifdef JSMN_STRICT
|
||||
/* Unexpected char in strict mode */
|
||||
default:
|
||||
return JSMN_ERROR_INVAL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens != NULL) {
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
/* Unmatched opened object or array */
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new parser based over a given buffer with an array of tokens
|
||||
* available.
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser) {
|
||||
parser->pos = 0;
|
||||
parser->toknext = 0;
|
||||
parser->toksuper = -1;
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright (c) 2010 Serge A. Zaitsev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.*
|
||||
*/
|
||||
#ifndef __JSMN_H_
|
||||
#define __JSMN_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* JSON type identifier. Basic types are:
|
||||
* o Object
|
||||
* o Array
|
||||
* o String
|
||||
* o Other primitive: number, boolean (true/false) or null
|
||||
*/
|
||||
typedef enum {
|
||||
JSMN_UNDEFINED = 0,
|
||||
JSMN_OBJECT = 1,
|
||||
JSMN_ARRAY = 2,
|
||||
JSMN_STRING = 3,
|
||||
JSMN_PRIMITIVE = 4
|
||||
} jsmntype_t;
|
||||
|
||||
enum jsmnerr {
|
||||
/* Not enough tokens were provided */
|
||||
JSMN_ERROR_NOMEM = -1,
|
||||
/* Invalid character inside JSON string */
|
||||
JSMN_ERROR_INVAL = -2,
|
||||
/* The string is not a full JSON packet, more bytes expected */
|
||||
JSMN_ERROR_PART = -3
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON token description.
|
||||
* @param type type (object, array, string etc.)
|
||||
* @param start start position in JSON data string
|
||||
* @param end end position in JSON data string
|
||||
*/
|
||||
typedef struct {
|
||||
jsmntype_t type;
|
||||
int start;
|
||||
int end;
|
||||
int size;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
int parent;
|
||||
#endif
|
||||
} jsmntok_t;
|
||||
|
||||
/**
|
||||
* JSON parser. Contains an array of token blocks available. Also stores
|
||||
* the string being parsed now and current position in that string
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int pos; /* offset in the JSON string */
|
||||
unsigned int toknext; /* next token to allocate */
|
||||
int toksuper; /* superior token node, e.g parent object or array */
|
||||
} jsmn_parser;
|
||||
|
||||
/**
|
||||
* Create JSON parser over an array of tokens
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser);
|
||||
|
||||
/**
|
||||
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
|
||||
* a single JSON object.
|
||||
*/
|
||||
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
||||
jsmntok_t *tokens, unsigned int num_tokens);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __JSMN_H_ */
|
|
@ -0,0 +1,319 @@
|
|||
/* $OpenBSD: json.c,v 1.9 2017/01/24 13:32:55 jsing Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* 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 AUTHORS DISCLAIM ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <assert.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "jsmn.h"
|
||||
#include "main.h"
|
||||
|
||||
struct jsmnp;
|
||||
|
||||
/*
|
||||
* Objects consist of node pairs: the left-hand side (before the colon)
|
||||
* and the right-hand side---the data.
|
||||
*/
|
||||
struct jsmnp {
|
||||
struct jsmnn *lhs; /* left of colon */
|
||||
struct jsmnn *rhs; /* right of colon */
|
||||
};
|
||||
|
||||
/*
|
||||
* Object for converting the JSMN token array into a tree.
|
||||
*/
|
||||
struct parse {
|
||||
struct jsmnn *nodes; /* all nodes */
|
||||
size_t cur; /* current number */
|
||||
size_t max; /* nodes in "nodes" */
|
||||
};
|
||||
|
||||
/*
|
||||
* Recursive part for convertin a JSMN token array into a tree.
|
||||
* See "example/jsondump.c" for its construction (it's the same except
|
||||
* for how it handles allocation errors).
|
||||
*/
|
||||
static ssize_t
|
||||
build(struct parse *parse, struct jsmnn **np,
|
||||
jsmntok_t *t, const char *js, size_t sz)
|
||||
{
|
||||
size_t i, j;
|
||||
struct jsmnn *n;
|
||||
ssize_t tmp;
|
||||
|
||||
if (sz == 0)
|
||||
return 0;
|
||||
|
||||
assert(parse->cur < parse->max);
|
||||
n = *np = &parse->nodes[parse->cur++];
|
||||
n->p = parse;
|
||||
n->type = t->type;
|
||||
|
||||
switch (t->type) {
|
||||
case JSMN_STRING:
|
||||
/* FALLTHROUGH */
|
||||
case JSMN_PRIMITIVE:
|
||||
n->fields = 1;
|
||||
n->d.str = strndup
|
||||
(js + t->start,
|
||||
t->end - t->start);
|
||||
if (n->d.str == NULL)
|
||||
break;
|
||||
return 1;
|
||||
case JSMN_OBJECT:
|
||||
n->fields = t->size;
|
||||
n->d.obj = calloc(n->fields,
|
||||
sizeof(struct jsmnp));
|
||||
if (n->d.obj == NULL)
|
||||
break;
|
||||
for (i = j = 0; i < (size_t)t->size; i++) {
|
||||
tmp = build(parse,
|
||||
&n->d.obj[i].lhs,
|
||||
t + 1 + j, js, sz - j);
|
||||
if (tmp < 0)
|
||||
break;
|
||||
j += tmp;
|
||||
tmp = build(parse,
|
||||
&n->d.obj[i].rhs,
|
||||
t + 1 + j, js, sz - j);
|
||||
if (tmp < 0)
|
||||
break;
|
||||
j += tmp;
|
||||
}
|
||||
if (i < (size_t)t->size)
|
||||
break;
|
||||
return j + 1;
|
||||
case JSMN_ARRAY:
|
||||
n->fields = t->size;
|
||||
n->d.array = calloc(n->fields,
|
||||
sizeof(struct jsmnn *));
|
||||
if (n->d.array == NULL)
|
||||
break;
|
||||
for (i = j = 0; i < (size_t)t->size; i++) {
|
||||
tmp = build(parse,
|
||||
&n->d.array[i],
|
||||
t + 1 + j, js, sz - j);
|
||||
if (tmp < 0)
|
||||
break;
|
||||
j += tmp;
|
||||
}
|
||||
if (i < (size_t)t->size)
|
||||
break;
|
||||
return j + 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fully free up a parse sequence.
|
||||
* This handles all nodes sequentially, not recursively.
|
||||
*/
|
||||
static void
|
||||
jsmnparse_free(struct parse *p)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (p == NULL)
|
||||
return;
|
||||
for (i = 0; i < p->max; i++) {
|
||||
struct jsmnn *n = &p->nodes[i];
|
||||
switch (n->type) {
|
||||
case JSMN_ARRAY:
|
||||
free(n->d.array);
|
||||
break;
|
||||
case JSMN_OBJECT:
|
||||
free(n->d.obj);
|
||||
break;
|
||||
case JSMN_PRIMITIVE:
|
||||
free(n->d.str);
|
||||
break;
|
||||
case JSMN_STRING:
|
||||
free(n->d.str);
|
||||
break;
|
||||
case JSMN_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(p->nodes);
|
||||
free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a tree representation of "t".
|
||||
* This returns NULL on allocation failure or when sz is zero, in which
|
||||
* case all resources allocated along the way are freed already.
|
||||
*/
|
||||
static struct jsmnn *
|
||||
jsmntree_alloc(jsmntok_t *t, const char *js, size_t sz)
|
||||
{
|
||||
struct jsmnn *first;
|
||||
struct parse *p;
|
||||
|
||||
if (sz == 0)
|
||||
return NULL;
|
||||
|
||||
p = calloc(1, sizeof(struct parse));
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
|
||||
p->max = sz;
|
||||
p->nodes = calloc(p->max, sizeof(struct jsmnn));
|
||||
if (p->nodes == NULL) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (build(p, &first, t, js, sz) < 0) {
|
||||
jsmnparse_free(p);
|
||||
first = NULL;
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call through to free parse contents.
|
||||
*/
|
||||
void
|
||||
json_free(struct jsmnn *first)
|
||||
{
|
||||
|
||||
if (first != NULL)
|
||||
jsmnparse_free(first->p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just check that the array object is in fact an object.
|
||||
*/
|
||||
struct jsmnn *
|
||||
json_getarrayobj(struct jsmnn *n)
|
||||
{
|
||||
|
||||
return n->type != JSMN_OBJECT ? NULL : n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract an array from the returned JSON object, making sure that it's
|
||||
* the correct type.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct jsmnn *
|
||||
json_getarray(struct jsmnn *n, const char *name)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (n->type != JSMN_OBJECT)
|
||||
return NULL;
|
||||
for (i = 0; i < n->fields; i++) {
|
||||
if (n->d.obj[i].lhs->type != JSMN_STRING &&
|
||||
n->d.obj[i].lhs->type != JSMN_PRIMITIVE)
|
||||
continue;
|
||||
else if (strcmp(name, n->d.obj[i].lhs->d.str))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (i == n->fields)
|
||||
return NULL;
|
||||
if (n->d.obj[i].rhs->type != JSMN_ARRAY)
|
||||
return NULL;
|
||||
return n->d.obj[i].rhs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a single string from the returned JSON object, making sure
|
||||
* that it's the correct type.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
char *
|
||||
json_getstr(struct jsmnn *n, const char *name)
|
||||
{
|
||||
size_t i;
|
||||
char *cp;
|
||||
|
||||
if (n->type != JSMN_OBJECT)
|
||||
return NULL;
|
||||
for (i = 0; i < n->fields; i++) {
|
||||
if (n->d.obj[i].lhs->type != JSMN_STRING &&
|
||||
n->d.obj[i].lhs->type != JSMN_PRIMITIVE)
|
||||
continue;
|
||||
else if (strcmp(name, n->d.obj[i].lhs->d.str))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (i == n->fields)
|
||||
return NULL;
|
||||
if (n->d.obj[i].rhs->type != JSMN_STRING &&
|
||||
n->d.obj[i].rhs->type != JSMN_PRIMITIVE)
|
||||
return NULL;
|
||||
|
||||
cp = strdup(n->d.obj[i].rhs->d.str);
|
||||
if (cp == NULL)
|
||||
warn("strdup");
|
||||
return cp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an HTTP response body from a buffer of size "sz".
|
||||
* Returns an opaque pointer on success, otherwise NULL on error.
|
||||
*/
|
||||
struct jsmnn *
|
||||
json_parse(const char *buf, size_t sz)
|
||||
{
|
||||
struct jsmnn *n;
|
||||
jsmn_parser p;
|
||||
jsmntok_t *tok;
|
||||
int r;
|
||||
size_t tokcount;
|
||||
|
||||
jsmn_init(&p);
|
||||
tokcount = 128;
|
||||
|
||||
/* Do this until we don't need any more tokens. */
|
||||
again:
|
||||
tok = calloc(tokcount, sizeof(jsmntok_t));
|
||||
if (tok == NULL) {
|
||||
warn("calloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Actually try to parse the JSON into the tokens. */
|
||||
|
||||
r = jsmn_parse(&p, buf, sz, tok, tokcount);
|
||||
if (r < 0 && r == JSMN_ERROR_NOMEM) {
|
||||
tokcount *= 2;
|
||||
free(tok);
|
||||
goto again;
|
||||
} else if (r < 0) {
|
||||
warnx("jsmn_parse: %d", r);
|
||||
free(tok);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Now parse the tokens into a tree. */
|
||||
|
||||
n = jsmntree_alloc(tok, buf, r);
|
||||
free(tok);
|
||||
return n;
|
||||
}
|
892
agent/main.c
892
agent/main.c
File diff suppressed because it is too large
Load Diff
116
agent/main.h
116
agent/main.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
||||
* Copyright (c) 2017, 2018, 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
|
||||
|
@ -19,10 +19,26 @@
|
|||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "http.h"
|
||||
#include "jsmn.h"
|
||||
|
||||
#define DEFAULT_ENDPOINT "169.254.169.254"
|
||||
#define CONNECT_TIMEOUT 10 /* in seconds */
|
||||
|
||||
enum cloudname {
|
||||
AZURE,
|
||||
CLOUDINIT,
|
||||
EC2,
|
||||
OPENNEBULA,
|
||||
OPENSTACK
|
||||
};
|
||||
#define CLOUDNAMES { \
|
||||
"azure", "cloudinit", "ec2", "opennebula", "openstack", NULL \
|
||||
}
|
||||
|
||||
enum strtype {
|
||||
WORD,
|
||||
|
@ -30,6 +46,13 @@ enum strtype {
|
|||
TEXT
|
||||
};
|
||||
|
||||
enum state {
|
||||
STATE_INIT,
|
||||
STATE_DHCP,
|
||||
STATE_169,
|
||||
STATE_DONE
|
||||
};
|
||||
|
||||
struct ssh_pubkey {
|
||||
char *ssh_keyval;
|
||||
char *ssh_keyfp;
|
||||
|
@ -38,26 +61,91 @@ struct ssh_pubkey {
|
|||
};
|
||||
TAILQ_HEAD(ssh_pubkeys, ssh_pubkey);
|
||||
|
||||
enum net_type {
|
||||
NET_IP,
|
||||
NET_MASK,
|
||||
NET_PREFIX,
|
||||
NET_MAC,
|
||||
NET_MTU,
|
||||
NET_GATEWAY,
|
||||
NET_DNS,
|
||||
NET_DNS_DOMAIN,
|
||||
NET_MAX
|
||||
};
|
||||
|
||||
struct net_addr {
|
||||
enum net_type net_type;
|
||||
unsigned short net_ifunit;
|
||||
char *net_value;
|
||||
struct sockaddr_storage net_addr;
|
||||
unsigned int net_num;
|
||||
|
||||
TAILQ_ENTRY(net_addr) net_entry;
|
||||
};
|
||||
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 {
|
||||
const char *sc_stack;
|
||||
char *sc_args;
|
||||
|
||||
char *sc_hostname;
|
||||
char *sc_username;
|
||||
char *sc_password;
|
||||
char *sc_password_plain;
|
||||
char *sc_password_hash;
|
||||
char *sc_pubkey;
|
||||
char *sc_userdata;
|
||||
char *sc_endpoint;
|
||||
enum state sc_state;
|
||||
struct clouds *sc_clouds;
|
||||
char *sc_instance;
|
||||
int sc_timeout;
|
||||
|
||||
const char *sc_ovfenv;
|
||||
const char *sc_interface;
|
||||
const char *sc_cdrom;
|
||||
const char *sc_rootdisk;
|
||||
int sc_mount;
|
||||
|
||||
struct source sc_addr;
|
||||
struct ssh_pubkeys sc_pubkeys;
|
||||
|
||||
int sc_network;
|
||||
struct net_addrs sc_netaddrs;
|
||||
unsigned int sc_netmtu;
|
||||
|
||||
int sc_nullfd;
|
||||
int sc_dryrun;
|
||||
void *sc_priv;
|
||||
};
|
||||
|
||||
struct jsmnp;
|
||||
struct jsmnn {
|
||||
struct parse *p;
|
||||
union {
|
||||
char *str;
|
||||
struct jsmnp *obj;
|
||||
struct jsmnn **array;
|
||||
} d;
|
||||
size_t fields;
|
||||
jsmntype_t type;
|
||||
};
|
||||
|
||||
/* json.c */
|
||||
struct jsmnn *json_parse(const char *, size_t);
|
||||
void json_free(struct jsmnn *);
|
||||
struct jsmnn *json_getarrayobj(struct jsmnn *);
|
||||
struct jsmnn *json_getarray(struct jsmnn *, const char *);
|
||||
struct jsmnn *json_getobj(struct jsmnn *, const char *);
|
||||
char *json_getstr(struct jsmnn *, const char *);
|
||||
|
||||
/* azure.c */
|
||||
int azure(struct system_config *);
|
||||
|
||||
|
@ -65,17 +153,33 @@ int azure(struct system_config *);
|
|||
int ec2(struct system_config *);
|
||||
int cloudinit(struct system_config *);
|
||||
|
||||
/* opennebula.c */
|
||||
int opennebula(struct system_config *);
|
||||
|
||||
/* openstack.c */
|
||||
int openstack(struct system_config *);
|
||||
|
||||
/* growdisk.c */
|
||||
int growdisk(struct system_config *);
|
||||
|
||||
/* main.c */
|
||||
int shell(const char *, ...);
|
||||
int shellout(const char *, char **, const char *, ...);
|
||||
int disable_output(struct system_config *, int);
|
||||
int enable_output(struct system_config *, int, int);
|
||||
char *get_string(u_int8_t *, size_t);
|
||||
char *get_line(u_int8_t *, size_t);
|
||||
char *get_word(u_int8_t *, size_t);
|
||||
char *get_string(const unsigned char *, size_t);
|
||||
char *get_line(const unsigned char *, size_t);
|
||||
char *get_word(const unsigned char *, size_t);
|
||||
int agent_addpubkey(struct system_config *, const char *, const char *);
|
||||
int agent_setpubkey(struct system_config *, const char *, const char *);
|
||||
int agent_configure(struct system_config *, int);
|
||||
struct net_addr *
|
||||
agent_getnetaddr(struct system_config *, struct net_addr *);
|
||||
int agent_addnetaddr(struct system_config *, unsigned int,
|
||||
const char *, int, enum net_type);
|
||||
char *metadata(struct system_config *, const char *, enum strtype);
|
||||
char *metadata_file(struct system_config *, const char *, enum strtype);
|
||||
int connect_wait(int, const struct sockaddr *, socklen_t);
|
||||
int dhcp_getendpoint(struct system_config *);
|
||||
|
||||
/* log.c */
|
||||
void log_init(int, int);
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fnmatch.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <sha2.h>
|
||||
#include <err.h>
|
||||
#include <util.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
int
|
||||
opennebula(struct system_config *sc)
|
||||
{
|
||||
FILE *fp;
|
||||
const char *delim = "\\\\\0", *errstr;
|
||||
char *line = NULL, *k, *v, *p, q;
|
||||
char *value = NULL, *next = NULL, *last;
|
||||
char *hname = NULL, *uname = NULL;
|
||||
size_t len, lineno = 0, i;
|
||||
int ret = -1;
|
||||
unsigned short unit;
|
||||
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
sc->sc_state = STATE_169;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Return silently without error */
|
||||
if ((fp = fopen("/mnt/context.sh", "r")) == NULL)
|
||||
goto done;
|
||||
|
||||
while ((line = fparseln(fp, &len, &lineno,
|
||||
delim, FPARSELN_UNESCALL)) != NULL) {
|
||||
/* key */
|
||||
k = line + strspn(line, " \t\r");
|
||||
|
||||
/* a context always starts with this header */
|
||||
if (lineno == 1) {
|
||||
ret = strcmp(k,
|
||||
"# Context variables generated by OpenNebula");
|
||||
if (ret != 0) {
|
||||
log_debug("%s: unsupported context", __func__);
|
||||
goto done;
|
||||
}
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Strip comments that do not occur within a value */
|
||||
if (*k == '#') {
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* value */
|
||||
if ((v = strchr(line, '=')) == NULL || *(v + 1) == '\0') {
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
*v++ = '\0';
|
||||
|
||||
/* value is quoted */
|
||||
q = *v;
|
||||
if (strspn(v, "\"'") == 0) {
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
*v++ = '\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 */
|
||||
if (*v == '\0') {
|
||||
free(line);
|
||||
free(value);
|
||||
value = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* print key/value unless it is a multi-line value */
|
||||
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("YES", v) == 0)
|
||||
sc->sc_network = 1;
|
||||
else if (strcasecmp("YES", v) == 0)
|
||||
sc->sc_network = 0;
|
||||
} else if (fnmatch("ETH*_*", k, 0) != FNM_NOMATCH) {
|
||||
/* Extract interface unit */
|
||||
if ((p = strdup(k + 3)) == NULL) {
|
||||
log_debug("%s: %s", __func__, k);
|
||||
goto done;
|
||||
}
|
||||
p[strcspn(p, "_")] = '\0';
|
||||
unit = strtonum(p, 0, UINT16_MAX, &errstr);
|
||||
free(p);
|
||||
if (errstr != NULL) {
|
||||
log_debug("%s: %s", __func__, k);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Get subkey */
|
||||
k += strcspn(k, "_") + 1;
|
||||
|
||||
if (strcasecmp("DNS", k) == 0) {
|
||||
/* We don't support per-interface DNS */
|
||||
for (p = v; *p != '\0'; v = p) {
|
||||
p = v + strcspn(v, " \t");
|
||||
*p++ = '\0';
|
||||
if ((ret = agent_addnetaddr(sc, 0,
|
||||
v, AF_UNSPEC, NET_DNS)) != 0)
|
||||
break;
|
||||
}
|
||||
} else if (strcasecmp("SEARCH_DOMAIN", k) == 0) {
|
||||
for (p = v; *p != '\0'; v = p) {
|
||||
p = v + strcspn(v, " \t");
|
||||
*p++ = '\0';
|
||||
if ((ret = agent_addnetaddr(sc, 0,
|
||||
v, AF_UNSPEC, NET_DNS_DOMAIN)) != 0)
|
||||
break;
|
||||
}
|
||||
} else if (strcasecmp("IP", k) == 0) {
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_INET, NET_IP);
|
||||
} else if (strcasecmp("MASK", k) == 0) {
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_INET, NET_MASK);
|
||||
} else if (strcasecmp("GATEWAY", k) == 0) {
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_INET, NET_GATEWAY);
|
||||
} else if (strcasecmp("IP6", k) == 0) {
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_INET6, NET_IP);
|
||||
} else if (strcasecmp("GATEWAY6", k) == 0) {
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_INET6, NET_GATEWAY);
|
||||
} else if (strcasecmp("PREFIX_LENGTH", k) == 0) {
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_INET6, NET_PREFIX);
|
||||
} else if (strcasecmp("MAC", k) == 0) {
|
||||
if (unit == 0 && hname == NULL) {
|
||||
/* Fake a hostname using the mac */
|
||||
if ((hname = p = calloc(1,
|
||||
strlen(v) + 3)) == NULL) {
|
||||
log_debug("%s: calloc",
|
||||
__func__);
|
||||
goto done;
|
||||
}
|
||||
*p++ = 'v';
|
||||
*p++ = 'm';
|
||||
for (i = 0; i < strlen(v); i++) {
|
||||
if (!isalnum(v[i]))
|
||||
continue;
|
||||
*p++ = v[i];
|
||||
}
|
||||
}
|
||||
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_UNSPEC, NET_MAC);
|
||||
} else if (strcasecmp("MTU", k) == 0) {
|
||||
ret = agent_addnetaddr(sc, unit,
|
||||
v, AF_UNSPEC, NET_MTU);
|
||||
} else
|
||||
ret = 0;
|
||||
if (ret != 0) {
|
||||
log_debug("%s: failed to parse %s",
|
||||
__func__, k);
|
||||
goto done;
|
||||
}
|
||||
} else if (strcasecmp("HOSTNAME", k) == 0) {
|
||||
if ((hname = strdup(v)) == NULL)
|
||||
log_warnx("failed to set hostname");
|
||||
} else if (strcasecmp("SSH_PUBLIC_KEY", k) == 0) {
|
||||
do {
|
||||
p = v + strcspn(v, "\n");
|
||||
*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(value);
|
||||
value = NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
|
||||
/*
|
||||
* OpenNebula doesn't provide an instance id so we
|
||||
* calculate one using the hash of the context file.
|
||||
* This might break if the context is not consistent.
|
||||
*/
|
||||
if ((sc->sc_instance =
|
||||
calloc(1, SHA256_DIGEST_STRING_LENGTH)) == NULL ||
|
||||
SHA256File("/mnt/context.sh", sc->sc_instance) == NULL) {
|
||||
log_debug("%s: failed to calculate instance hash",
|
||||
__func__);
|
||||
goto done;
|
||||
}
|
||||
log_debug("%s: context instance %s", __func__, sc->sc_instance);
|
||||
|
||||
/* Even the hostname is optional */
|
||||
if (hname != NULL) {
|
||||
free(sc->sc_hostname);
|
||||
sc->sc_hostname = hname;
|
||||
log_debug("%s: hostname %s", __func__, hname);
|
||||
}
|
||||
|
||||
line = NULL;
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
if (fp != NULL)
|
||||
fclose(fp);
|
||||
free(line);
|
||||
free(value);
|
||||
return (ret);
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 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/queue.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "http.h"
|
||||
#include "xml.h"
|
||||
|
||||
static int openstack_fetch(struct system_config *);
|
||||
|
||||
int
|
||||
openstack(struct system_config *sc)
|
||||
{
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
sc->sc_state = STATE_DHCP;
|
||||
return (-1);
|
||||
}
|
||||
return openstack_fetch(sc);
|
||||
}
|
||||
|
||||
static int
|
||||
openstack_fetch(struct system_config *sc)
|
||||
{
|
||||
int ret = -1;
|
||||
char *json = NULL, *str;
|
||||
struct jsmnn *j = NULL, *o, *f;
|
||||
size_t i;
|
||||
|
||||
sc->sc_addr.ip = sc->sc_endpoint;
|
||||
sc->sc_addr.family = 4;
|
||||
|
||||
/* meta_data, we don't handle vendor_data */
|
||||
if ((json = metadata(sc,
|
||||
"/openstack/latest/meta_data.json", TEXT)) == NULL)
|
||||
goto fail;
|
||||
|
||||
if ((j = json_parse(json, strlen(json))) == NULL)
|
||||
goto fail;
|
||||
|
||||
/* instance-id */
|
||||
if ((sc->sc_instance = json_getstr(j, "uuid")) == NULL)
|
||||
goto fail;
|
||||
|
||||
/* hostname */
|
||||
if ((sc->sc_hostname = json_getstr(j, "hostname")) == NULL)
|
||||
goto fail;
|
||||
|
||||
/* public keys */
|
||||
if ((o = json_getarray(j, "keys")) == NULL)
|
||||
goto fail;
|
||||
for (i = 0; i < o->fields; i++) {
|
||||
if ((f = json_getarrayobj(o->d.array[i])) == NULL)
|
||||
continue;
|
||||
if ((str = json_getstr(f, "data")) == NULL)
|
||||
continue;
|
||||
if (agent_addpubkey(sc, str, NULL) != 0) {
|
||||
free(str);
|
||||
goto fail;
|
||||
}
|
||||
free(str);
|
||||
}
|
||||
|
||||
/* userdata (optional) */
|
||||
sc->sc_userdata = metadata(sc, "/openstack/latest/user_data", TEXT);
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
json_free(j);
|
||||
free(json);
|
||||
return (ret);
|
||||
}
|
|
@ -342,9 +342,10 @@ xml_parse(struct xml *env, const char *file)
|
|||
ssize_t len;
|
||||
|
||||
if ((fd = open(file, O_RDONLY)) == -1) {
|
||||
log_debug("%s: open %s", __func__, file);
|
||||
log_debug("%s: failed to open %s", __func__, file);
|
||||
return (-1);
|
||||
}
|
||||
} else
|
||||
log_debug("%s: opened %s", __func__, file);
|
||||
|
||||
do {
|
||||
if ((xml = XML_GetBuffer(parser, BUFSIZ)) == NULL)
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
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
|
|
@ -80,6 +80,13 @@ DECLARE_ASN1_FUNCTIONS(CMS_ContentInfo)
|
|||
DECLARE_ASN1_FUNCTIONS(CMS_ReceiptRequest)
|
||||
DECLARE_ASN1_PRINT_FUNCTION(CMS_ContentInfo)
|
||||
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20900000L
|
||||
#define M_ASN1_new_of(type) \
|
||||
(type *)ASN1_item_new(ASN1_ITEM_rptr(type))
|
||||
#define M_ASN1_free_of(x, type) \
|
||||
ASN1_item_free(CHECKED_PTR_OF(type, x), ASN1_ITEM_rptr(type))
|
||||
#endif
|
||||
|
||||
# define CMS_SIGNERINFO_ISSUER_SERIAL 0
|
||||
# define CMS_SIGNERINFO_KEYIDENTIFIER 1
|
||||
|
||||
|
|
|
@ -353,7 +353,11 @@ void cms_DigestAlgorithm_set(X509_ALGOR *alg, const EVP_MD *md)
|
|||
BIO *cms_DigestAlgorithm_init_bio(X509_ALGOR *digestAlgorithm)
|
||||
{
|
||||
BIO *mdbio = NULL;
|
||||
#if LIBRESSL_VERSION_NUMBER >= 0x2080000fL
|
||||
const ASN1_OBJECT *digestoid;
|
||||
#else
|
||||
ASN1_OBJECT *digestoid;
|
||||
#endif
|
||||
const EVP_MD *digest;
|
||||
X509_ALGOR_get0(&digestoid, NULL, NULL, digestAlgorithm);
|
||||
digest = EVP_get_digestbyobj(digestoid);
|
||||
|
@ -380,7 +384,11 @@ int cms_DigestAlgorithm_find_ctx(EVP_MD_CTX *mctx, BIO *chain,
|
|||
X509_ALGOR *mdalg)
|
||||
{
|
||||
int nid;
|
||||
#if LIBRESSL_VERSION_NUMBER >= 0x2080000fL
|
||||
const ASN1_OBJECT *mdoid;
|
||||
#else
|
||||
ASN1_OBJECT *mdoid;
|
||||
#endif
|
||||
X509_ALGOR_get0(&mdoid, NULL, NULL, mdalg);
|
||||
nid = OBJ_obj2nid(mdoid);
|
||||
/* Look for digest type to match signature */
|
||||
|
|
|
@ -328,7 +328,11 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
|
|||
|
||||
/* See if digest is present in digestAlgorithms */
|
||||
for (i = 0; i < sk_X509_ALGOR_num(sd->digestAlgorithms); i++) {
|
||||
#if LIBRESSL_VERSION_NUMBER >= 0x2080000fL
|
||||
const ASN1_OBJECT *aoid;
|
||||
#else
|
||||
ASN1_OBJECT *aoid;
|
||||
#endif
|
||||
alg = sk_X509_ALGOR_value(sd->digestAlgorithms, i);
|
||||
X509_ALGOR_get0(&aoid, NULL, NULL, alg);
|
||||
if (OBJ_obj2nid(aoid) == EVP_MD_type(md))
|
||||
|
|
Loading…
Reference in New Issue