Compare commits
44 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 |
|
@ -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= azure.c cloudinit.c http.c json.c jsmn.c log.c openstack.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+= 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>
|
||||
|
|
|
@ -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"
|
||||
|
@ -62,55 +65,62 @@ azure(struct system_config *sc)
|
|||
{
|
||||
int ret = -1;
|
||||
|
||||
/* Apply defaults */
|
||||
free(sc->sc_username);
|
||||
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
||||
log_warnx("failed to set default user");
|
||||
goto done;
|
||||
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);
|
||||
}
|
||||
|
||||
/* Apply defaults */
|
||||
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
||||
sc->sc_priv = &az_config;
|
||||
sc->sc_state = STATE_DHCP;
|
||||
return (-1);
|
||||
}
|
||||
sc->sc_cdrom = "/dev/cd0c";
|
||||
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
||||
sc->sc_priv = &az_config;
|
||||
|
||||
/* 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 done;
|
||||
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 (!sc->sc_dryrun) {
|
||||
if (azure_keys(sc) != 0) {
|
||||
log_warnx("failed to get transport keys");
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (azure_certificates(sc) != 0) {
|
||||
log_warnx("failed to get certificates");
|
||||
goto done;
|
||||
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);
|
||||
|
||||
|
@ -433,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
|
||||
|
@ -654,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) {
|
||||
|
@ -732,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;
|
||||
}
|
||||
|
@ -740,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 */
|
||||
|
@ -756,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;
|
||||
|
|
|
@ -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
|
||||
|
@ -34,32 +34,32 @@ static int cloudinit_fetch(struct system_config *);
|
|||
int
|
||||
ec2(struct system_config *sc)
|
||||
{
|
||||
free(sc->sc_username);
|
||||
if ((sc->sc_username = strdup("ec2-user")) == NULL ||
|
||||
(sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == 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)
|
||||
{
|
||||
if ((dhcp_getendpoint(sc) == -1) &&
|
||||
(sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
|
||||
log_warnx("failed to set defaults");
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
sc->sc_state = STATE_DHCP;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (cloudinit_fetch(sc));
|
||||
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;
|
||||
|
@ -75,11 +75,13 @@ cloudinit_fetch(struct system_config *sc)
|
|||
"/latest/meta-data/local-hostname", WORD)) == NULL)
|
||||
goto fail;
|
||||
|
||||
/* pubkey */
|
||||
/* optional pubkey */
|
||||
if ((str = metadata(sc,
|
||||
"/latest/meta-data/public-keys/0/openssh-key", LINE)) == NULL)
|
||||
goto fail;
|
||||
if (agent_addpubkey(sc, str, NULL) != 0)
|
||||
"/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) */
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
682
agent/main.c
682
agent/main.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
|
||||
|
@ -15,32 +15,48 @@
|
|||
*/
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <resolv.h>
|
||||
#include <netdb.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "xml.h"
|
||||
|
||||
__dead void usage(void);
|
||||
static struct system_config *agent_init(const char *, int, int);
|
||||
static int agent_configure(struct system_config *);
|
||||
static void agent_free(struct system_config *);
|
||||
static int agent_pf(struct system_config *, int);
|
||||
static void agent_unconfigure(void);
|
||||
static char *metadata_parse(char *, size_t, enum strtype);
|
||||
static int agent_timeout;
|
||||
__dead void usage(void);
|
||||
static struct system_config
|
||||
*agent_init(const char *, const char *, int, int);
|
||||
static int agent_configure(struct system_config *);
|
||||
static int agent_network(struct system_config *);
|
||||
static void agent_free(struct system_config *);
|
||||
static int agent_pf(struct system_config *, int);
|
||||
static int agent_userdata(struct system_config *,
|
||||
const unsigned char *, size_t);
|
||||
static void agent_unconfigure(void);
|
||||
static char *metadata_parse(char *, size_t, enum strtype);
|
||||
|
||||
static int agent_timeout;
|
||||
static char *cloudnames[] = CLOUDNAMES;
|
||||
|
||||
int
|
||||
shell(const char *arg, ...)
|
||||
|
@ -247,7 +263,7 @@ enable_output(struct system_config *sc, int fd, int oldfd)
|
|||
}
|
||||
|
||||
char *
|
||||
get_string(u_int8_t *ptr, size_t len)
|
||||
get_string(const unsigned char *ptr, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
|
@ -265,7 +281,7 @@ get_string(u_int8_t *ptr, size_t len)
|
|||
}
|
||||
|
||||
char *
|
||||
get_line(u_int8_t *ptr, size_t len)
|
||||
get_line(const unsigned char *ptr, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
|
@ -280,7 +296,7 @@ get_line(u_int8_t *ptr, size_t len)
|
|||
}
|
||||
|
||||
char *
|
||||
get_word(u_int8_t *ptr, size_t len)
|
||||
get_word(const unsigned char *ptr, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
|
@ -294,26 +310,34 @@ get_word(u_int8_t *ptr, size_t len)
|
|||
}
|
||||
|
||||
static struct system_config *
|
||||
agent_init(const char *ifname, int dryrun, int timeout)
|
||||
agent_init(const char *ifname, const char *rootdisk, int dryrun, int timeout)
|
||||
{
|
||||
struct system_config *sc;
|
||||
int fd, ret;
|
||||
|
||||
if ((sc = calloc(1, sizeof(*sc))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
sc->sc_interface = ifname;
|
||||
sc->sc_cdrom = "/dev/cd0c";
|
||||
sc->sc_dryrun = dryrun ? 1 : 0;
|
||||
sc->sc_timeout = agent_timeout = timeout < 1 ? -1 : timeout * 1000;
|
||||
sc->sc_rootdisk = rootdisk;
|
||||
TAILQ_INIT(&sc->sc_pubkeys);
|
||||
TAILQ_INIT(&sc->sc_netaddrs);
|
||||
|
||||
if ((sc->sc_nullfd = open("/dev/null", O_RDWR)) == -1) {
|
||||
free(sc);
|
||||
return (NULL);
|
||||
}
|
||||
if ((sc->sc_username = strdup("puffy")) == NULL) {
|
||||
free(sc);
|
||||
close(sc->sc_nullfd);
|
||||
return (NULL);
|
||||
|
||||
/* 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);
|
||||
if (ret == 0) {
|
||||
log_debug("%s: mounted %s", __func__, sc->sc_cdrom);
|
||||
sc->sc_mount = 1;
|
||||
}
|
||||
|
||||
if (sc->sc_dryrun)
|
||||
|
@ -331,13 +355,26 @@ static void
|
|||
agent_free(struct system_config *sc)
|
||||
{
|
||||
struct ssh_pubkey *ssh;
|
||||
struct net_addr *net;
|
||||
|
||||
/* unmount if we mounted the cdrom before */
|
||||
if (sc->sc_mount && shell("umount", "/mnt", NULL) == 0) {
|
||||
log_debug("%s: unmounted %s", __func__, sc->sc_cdrom);
|
||||
}
|
||||
|
||||
if (sc->sc_password_plain != NULL) {
|
||||
/* XXX can be removed with calloc_conceal() post-6.6 */
|
||||
explicit_bzero(sc->sc_password_plain,
|
||||
strlen(sc->sc_password_plain));
|
||||
free(sc->sc_password_plain);
|
||||
}
|
||||
free(sc->sc_password_hash);
|
||||
free(sc->sc_hostname);
|
||||
free(sc->sc_username);
|
||||
free(sc->sc_password);
|
||||
free(sc->sc_userdata);
|
||||
free(sc->sc_endpoint);
|
||||
free(sc->sc_instance);
|
||||
free(sc->sc_args);
|
||||
close(sc->sc_nullfd);
|
||||
|
||||
while ((ssh = TAILQ_FIRST(&sc->sc_pubkeys))) {
|
||||
|
@ -346,6 +383,12 @@ agent_free(struct system_config *sc)
|
|||
TAILQ_REMOVE(&sc->sc_pubkeys, ssh, ssh_entry);
|
||||
free(ssh);
|
||||
}
|
||||
|
||||
while ((net = TAILQ_FIRST(&sc->sc_netaddrs))) {
|
||||
TAILQ_REMOVE(&sc->sc_netaddrs, net, net_entry);
|
||||
free(net->net_value);
|
||||
free(net);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -399,6 +442,114 @@ agent_setpubkey(struct system_config *sc, const char *sshval, const char *sshfp)
|
|||
return (ret);
|
||||
}
|
||||
|
||||
struct net_addr *
|
||||
agent_getnetaddr(struct system_config *sc, struct net_addr *net)
|
||||
{
|
||||
struct net_addr *na;
|
||||
|
||||
TAILQ_FOREACH(na, &sc->sc_netaddrs, net_entry) {
|
||||
if (na->net_type != net->net_type)
|
||||
continue;
|
||||
if (na->net_ifunit != net->net_ifunit)
|
||||
continue;
|
||||
if (na->net_type == NET_DNS_DOMAIN &&
|
||||
strcasecmp(na->net_value, net->net_value) != 0)
|
||||
continue;
|
||||
if (net->net_addr.ss_family != AF_UNSPEC) {
|
||||
if (na->net_addr.ss_family !=
|
||||
net->net_addr.ss_family)
|
||||
continue;
|
||||
if (memcmp(&na->net_addr, &net->net_addr,
|
||||
na->net_addr.ss_len) != 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
return (na);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
agent_addnetaddr(struct system_config *sc, unsigned int unit,
|
||||
const char *value, int af, enum net_type type)
|
||||
{
|
||||
const char *errstr;
|
||||
struct addrinfo hints, *res;
|
||||
struct net_addr *net, *na;
|
||||
|
||||
if ((net = calloc(1, sizeof(*net))) == NULL) {
|
||||
log_debug("%s: calloc", __func__);
|
||||
return (-1);
|
||||
}
|
||||
net->net_ifunit = unit;
|
||||
net->net_type = type;
|
||||
|
||||
switch (type) {
|
||||
case NET_DNS_DOMAIN:
|
||||
if (strlen(value) >= NI_MAXHOST) {
|
||||
log_debug("%s: if%u domain %s", __func__, unit, value);
|
||||
free(net);
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
case NET_MAC:
|
||||
if (ether_aton(value) == NULL) {
|
||||
log_debug("%s: if%u mac %s", __func__, unit, value);
|
||||
free(net);
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
case NET_MTU:
|
||||
case NET_PREFIX:
|
||||
net->net_num = strtonum(value, 0, UINT32_MAX, &errstr);
|
||||
if (errstr != NULL) {
|
||||
log_debug("%s: if%u %s", __func__, unit, value);
|
||||
free(net);
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = af;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
if (getaddrinfo(value, "0", &hints, &res) != 0) {
|
||||
log_debug("%s: invalid address %s",
|
||||
__func__, value);
|
||||
free(net);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (res->ai_addrlen > sizeof(net->net_addr)) {
|
||||
log_debug("%s: address too long",
|
||||
__func__);
|
||||
free(net);
|
||||
freeaddrinfo(res);
|
||||
return (-1);
|
||||
}
|
||||
memcpy(&net->net_addr, res->ai_addr, res->ai_addrlen);
|
||||
net->net_addr.ss_len = res->ai_addrlen;
|
||||
net->net_addr.ss_family = res->ai_family;
|
||||
}
|
||||
|
||||
if ((net->net_value = strdup(value)) == NULL) {
|
||||
free(net);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Address already exists, ignore new entry */
|
||||
if ((na = agent_getnetaddr(sc, net)) != NULL) {
|
||||
free(net->net_value);
|
||||
free(net);
|
||||
return (0);
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&sc->sc_netaddrs, net, net_entry);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
fileout(const char *str, const char *mode, const char *fmt, ...)
|
||||
{
|
||||
|
@ -474,7 +625,7 @@ agent_pf(struct system_config *sc, int open)
|
|||
return (0);
|
||||
|
||||
if (open)
|
||||
ret = shellout("pass out proto tcp from egress to port www\n",
|
||||
ret = shellout("pass out proto tcp from (egress) to port www\n",
|
||||
NULL, "pfctl", "-f", "-", NULL);
|
||||
else
|
||||
ret = shellout("\n", NULL, "pfctl", "-f", "-", NULL);
|
||||
|
@ -485,6 +636,7 @@ agent_pf(struct system_config *sc, int open)
|
|||
static int
|
||||
agent_configure(struct system_config *sc)
|
||||
{
|
||||
char pwbuf[_PASSWORD_LEN + 2];
|
||||
struct ssh_pubkey *ssh;
|
||||
char *str1, *str2;
|
||||
|
||||
|
@ -501,6 +653,13 @@ agent_configure(struct system_config *sc)
|
|||
if (fileout(sc->sc_instance, "w", "/var/db/cloud-instance") != 0)
|
||||
log_warnx("instance failed");
|
||||
|
||||
/* Set default username if not set */
|
||||
if ((sc->sc_username == NULL) &&
|
||||
(sc->sc_username = strdup("puffy")) == NULL) {
|
||||
log_warn("default username");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* hostname */
|
||||
log_debug("%s: hostname %s", __func__, sc->sc_hostname);
|
||||
if (fileout(sc->sc_hostname, "w", "/etc/myname") != 0)
|
||||
|
@ -510,38 +669,49 @@ agent_configure(struct system_config *sc)
|
|||
|
||||
/* username */
|
||||
log_debug("%s: username %s", __func__, sc->sc_username);
|
||||
if (shell("useradd", "-L", "staff", "-G", "wheel",
|
||||
"-m", sc->sc_username, NULL) != 0)
|
||||
log_warnx("username failed");
|
||||
if (fileout(sc->sc_username, "w", "/root/.forward") != 0)
|
||||
log_warnx(".forward failed");
|
||||
if (strcmp("root", sc->sc_username) != 0) {
|
||||
if (shell("useradd", "-L", "staff", "-G", "wheel",
|
||||
"-m", sc->sc_username, NULL) != 0)
|
||||
log_warnx("username failed");
|
||||
if (fileout(sc->sc_username, "w", "/root/.forward") != 0)
|
||||
log_warnx(".forward failed");
|
||||
}
|
||||
|
||||
/* password */
|
||||
if (sc->sc_password == NULL) {
|
||||
str1 = "/PasswordAuthentication/"
|
||||
"s/.*/PasswordAuthentication no/";
|
||||
if (sc->sc_password_hash == NULL) {
|
||||
if (asprintf(&str2, "permit keepenv nopass %s as root\n"
|
||||
"permit keepenv nopass root\n", sc->sc_username) == -1)
|
||||
str2 = NULL;
|
||||
} else {
|
||||
if (shell("usermod", "-p", sc->sc_password,
|
||||
if (shell("usermod", "-p", sc->sc_password_hash,
|
||||
sc->sc_username, NULL) != 0)
|
||||
log_warnx("password failed");
|
||||
|
||||
str1 = "/PasswordAuthentication/"
|
||||
"s/.*/PasswordAuthentication yes/";
|
||||
if (asprintf(&str2, "permit keepenv persist %s as root\n"
|
||||
"permit keepenv nopass root\n", sc->sc_username) == -1)
|
||||
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 */
|
||||
if (str2 == NULL || fileout(str2, "w", "/etc/doas.conf") != 0)
|
||||
if ((strcmp("root", sc->sc_username) != 0) &&
|
||||
(str2 == NULL || fileout(str2, "w", "/etc/doas.conf")) != 0)
|
||||
log_warnx("doas failed");
|
||||
free(str2);
|
||||
|
||||
/* ssh configuration */
|
||||
if (sc->sc_password == NULL && !TAILQ_EMPTY(&sc->sc_pubkeys))
|
||||
if (sc->sc_password_hash == NULL && !TAILQ_EMPTY(&sc->sc_pubkeys))
|
||||
str1 = "/PasswordAuthentication/"
|
||||
"s/.*/PasswordAuthentication no/";
|
||||
else
|
||||
|
@ -558,15 +728,21 @@ agent_configure(struct system_config *sc)
|
|||
continue;
|
||||
log_debug("%s: key %s", __func__, ssh->ssh_keyval);
|
||||
if (fileout(ssh->ssh_keyval, "a",
|
||||
"/home/%s/.ssh/authorized_keys",
|
||||
"%s/%s/.ssh/authorized_keys",
|
||||
strcmp("root", sc->sc_username) == 0 ? "" : "/home",
|
||||
sc->sc_username) != 0)
|
||||
log_warnx("public key failed");
|
||||
}
|
||||
|
||||
if (sc->sc_userdata) {
|
||||
/* XXX */
|
||||
if (agent_userdata(sc, sc->sc_userdata,
|
||||
strlen(sc->sc_userdata)) != 0)
|
||||
log_warnx("user-data failed");
|
||||
}
|
||||
|
||||
if (agent_network(sc) != 0)
|
||||
log_warnx("network configuration failed");
|
||||
|
||||
log_debug("%s: %s", __func__, "/etc/rc.firsttime");
|
||||
if (fileout("logger -s -t cloud-agent <<EOF\n"
|
||||
"#############################################################\n"
|
||||
|
@ -582,6 +758,164 @@ agent_configure(struct system_config *sc)
|
|||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
agent_userdata(struct system_config *sc,
|
||||
const unsigned char *userdata, size_t len)
|
||||
{
|
||||
char *shebang = NULL, *str = NULL, *line = NULL;
|
||||
const char *file;
|
||||
int ret = -1;
|
||||
|
||||
if (len <= 2) {
|
||||
log_warnx("user-data too short");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (userdata[0] == 0x1f && userdata[1] == 0x8b) {
|
||||
log_warnx("gzip-compressed user-data is not supported");
|
||||
goto fail;
|
||||
} else if (userdata[0] == '#') {
|
||||
if ((shebang = get_line(userdata, len)) == NULL) {
|
||||
log_warnx("failed to decode shebang from user-data");
|
||||
goto fail;
|
||||
}
|
||||
} else if (isprint(userdata[0]) && isprint(userdata[1])) {
|
||||
/* Decode user-data and call the function again */
|
||||
if ((str = calloc(1, len + 1)) == NULL ||
|
||||
(len = b64_pton(userdata, str, len)) < 1 ||
|
||||
agent_userdata(sc, str, len) != 0) {
|
||||
log_warnx("failed to decode user-data");
|
||||
goto fail;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
log_debug("%s: user-data: %s", __func__, shebang);
|
||||
|
||||
if (strlen(shebang) <= 2 || strncmp("#!", shebang, 2) != 0) {
|
||||
log_warnx("unsupported user-data type");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* now get the whole script */
|
||||
if ((str = get_string(userdata, len)) == NULL) {
|
||||
log_warnx("invalid user-data script");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sc->sc_dryrun)
|
||||
goto done;
|
||||
|
||||
/* write user-data script into file */
|
||||
file = "/etc/rc.user-data";
|
||||
if (fileout(str, "w", file) != 0) {
|
||||
log_warnx("failed to write user-data");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* and call it from rc.firsttime later on boot */
|
||||
if (asprintf(&line,
|
||||
"logger -s -t cloud-agent \"running user-data\"\n"
|
||||
"%s %s\nrm %s\n", shebang + 2, file, file) == -1 ||
|
||||
fileout(line, "a", "/etc/rc.firsttime") != 0)
|
||||
log_warnx("failed to add user-data script");
|
||||
|
||||
done:
|
||||
ret = 0;
|
||||
fail:
|
||||
free(line);
|
||||
free(str);
|
||||
free(shebang);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
agent_network(struct system_config *sc)
|
||||
{
|
||||
struct net_addr *net;
|
||||
char ift[16], ifname[16], line[1024];
|
||||
const char *family;
|
||||
char domain[(NI_MAXHOST + 1) * 6 + 8]; /* up to 6 domains */
|
||||
int has_domain = 0;
|
||||
char ifidx[UINT16_MAX], *str;
|
||||
const char *comment = "# Generated by cloud-agent";
|
||||
|
||||
if (!sc->sc_network)
|
||||
return (0);
|
||||
|
||||
if (strlcpy(ift, sc->sc_interface, sizeof(ift)) >= sizeof(ift))
|
||||
return (-1);
|
||||
ift[strcspn(ift, "0123456789")] = '\0';
|
||||
|
||||
memset(ifidx, 0, sizeof(ifidx));
|
||||
snprintf(domain, sizeof(domain), "search ");
|
||||
fileout(comment, "w", "/etc/mygate");
|
||||
fileout(comment, "w", "/etc/resolv.conf");
|
||||
|
||||
TAILQ_FOREACH(net, &sc->sc_netaddrs, net_entry) {
|
||||
snprintf(ifname, sizeof(ifname), "%s%u", ift, net->net_ifunit);
|
||||
switch (net->net_type) {
|
||||
case NET_IP:
|
||||
family = net->net_addr.ss_family == AF_INET ?
|
||||
"inet" : "inet6";
|
||||
/* XXX prefix or mask */
|
||||
|
||||
/* hostname.if startup configuration */
|
||||
if (!ifidx[net->net_ifunit])
|
||||
fileout(comment, "w",
|
||||
"/etc/hostname.%s", ifname);
|
||||
|
||||
snprintf(line, sizeof(line), "%s alias %s",
|
||||
family, net->net_value);
|
||||
fileout(line, "a", "/etc/hostname.%s", ifname);
|
||||
|
||||
if (!ifidx[net->net_ifunit]++ &&
|
||||
net->net_ifunit == 0) {
|
||||
snprintf(line, sizeof(line),
|
||||
"!%s", sc->sc_args);
|
||||
fileout(line, "a", "/etc/hostname.%s", ifname);
|
||||
}
|
||||
|
||||
/* runtime configuration */
|
||||
(void)shell("ifconfig", ifname, family,
|
||||
"alias", net->net_value, NULL);
|
||||
break;
|
||||
case NET_GATEWAY:
|
||||
fileout(net->net_value, "a", "/etc/mygate");
|
||||
break;
|
||||
case NET_DNS:
|
||||
snprintf(line, sizeof(line), "nameserver %s",
|
||||
net->net_value);
|
||||
fileout(line, "a", "/etc/resolv.conf");
|
||||
break;
|
||||
case NET_DNS_DOMAIN:
|
||||
if (!has_domain++) {
|
||||
/* use the first search domain as our own */
|
||||
snprintf(line, sizeof(line), "domain %s",
|
||||
net->net_value);
|
||||
fileout(line, "a", "/etc/resolv.conf");
|
||||
} else
|
||||
(void)strlcat(domain, " ", sizeof(domain));
|
||||
(void)strlcat(domain, net->net_value, sizeof(domain));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_domain)
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
agent_unconfigure(void)
|
||||
{
|
||||
|
@ -608,8 +942,10 @@ agent_unconfigure(void)
|
|||
|
||||
(void)fileout("permit keepenv persist :wheel as root\n"
|
||||
"permit keepenv nopass root\n", "w", "/etc/doas.conf");
|
||||
}
|
||||
|
||||
/* Remove cloud-instance file */
|
||||
(void)unlink("/var/db/cloud-instance");
|
||||
}
|
||||
|
||||
static char *
|
||||
metadata_parse(char *s, size_t sz, enum strtype type)
|
||||
|
@ -638,10 +974,12 @@ metadata(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)
|
||||
log_debug("%s: HTTP %d http://%s%s", __func__, g->code,
|
||||
sc->sc_endpoint, path);
|
||||
|
||||
if (g != NULL && g->code == 200 && g->bodypartsz > 0)
|
||||
str = metadata_parse(g->bodypart, g->bodypartsz, type);
|
||||
http_get_free(g);
|
||||
|
@ -778,7 +1116,8 @@ dhcp_getendpoint(struct system_config *sc)
|
|||
sc->sc_addr.ip = sc->sc_endpoint;
|
||||
sc->sc_addr.family = 4;
|
||||
|
||||
log_debug("%s: %s", __func__, ep);
|
||||
if (log_getverbose() > 2)
|
||||
log_debug("%s: %s", __func__, ep);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -788,35 +1127,217 @@ usage(void)
|
|||
{
|
||||
extern char *__progname;
|
||||
|
||||
fprintf(stderr, "usage: %s [-nuv] [-t 3] interface\n",
|
||||
__progname);
|
||||
fprintf(stderr, "usage: %s [-nuv] "
|
||||
"[-c cloud[,...]] [-p length] [-r rootdisk]\n\t"
|
||||
"[-t 3] [-U puffy] interface\n", __progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_args(int argc, char *const *argv)
|
||||
{
|
||||
char *args, path[PATH_MAX];
|
||||
size_t argslen = 0;
|
||||
int i;
|
||||
|
||||
/* Store args in a string */
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (i == 0) {
|
||||
realpath(argv[0], path);
|
||||
argslen += strlen(path) + 1;
|
||||
} else {
|
||||
argslen += strlen(argv[i]) + 1;
|
||||
}
|
||||
}
|
||||
if ((args = calloc(1, argslen + 1)) == NULL)
|
||||
return (NULL);
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (i == 0)
|
||||
strlcat(args, path, argslen);
|
||||
else {
|
||||
strlcat(args, " ", argslen);
|
||||
strlcat(args, argv[i], argslen);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
main(int argc, char *const *argv)
|
||||
{
|
||||
struct system_config *sc;
|
||||
int verbose = 0, dryrun = 0, unconfigure = 0;
|
||||
int ch, ret, timeout = CONNECT_TIMEOUT;
|
||||
const char *error = NULL;
|
||||
int verbose = 0, dryrun = 0, unconfigure = 0, sub;
|
||||
int genpw = 0, ch, ret, timeout = CONNECT_TIMEOUT;
|
||||
const char *error = NULL, *rootdisk = NULL;
|
||||
char *args, *username = NULL, *options, *value;
|
||||
struct clouds clouds;
|
||||
|
||||
while ((ch = getopt(argc, argv, "nvt:u")) != -1) {
|
||||
/* log to stderr */
|
||||
log_init(1, LOG_DAEMON);
|
||||
|
||||
if ((args = get_args(argc, argv)) == NULL)
|
||||
fatalx("failed to save args");
|
||||
|
||||
TAILQ_INIT(&clouds);
|
||||
|
||||
while ((ch = getopt(argc, argv, "c:np:r:t:U:uv")) != -1) {
|
||||
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':
|
||||
dryrun = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose += 2;
|
||||
case 'p':
|
||||
genpw = strtonum(optarg, 8, 8192, &error);
|
||||
if (error != NULL)
|
||||
fatalx("invalid password length: %s", error);
|
||||
break;
|
||||
case 'r':
|
||||
rootdisk = optarg;
|
||||
break;
|
||||
case 't':
|
||||
timeout = strtonum(optarg, -1, 86400, &error);
|
||||
if (error != NULL)
|
||||
fatalx("invalid timeout: %s", error);
|
||||
break;
|
||||
case 'U':
|
||||
if ((username = strdup(optarg)) == NULL)
|
||||
fatal("username");
|
||||
break;
|
||||
case 'u':
|
||||
unconfigure = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose += 2;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
@ -825,8 +1346,6 @@ main(int argc, char *const *argv)
|
|||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
/* log to stderr */
|
||||
log_init(1, LOG_DAEMON);
|
||||
log_setverbose(verbose);
|
||||
|
||||
if (unconfigure) {
|
||||
|
@ -837,22 +1356,65 @@ main(int argc, char *const *argv)
|
|||
if (argc != 1)
|
||||
usage();
|
||||
|
||||
if (pledge("stdio cpath rpath wpath exec proc dns inet", NULL) == -1)
|
||||
if (pledge(rootdisk == NULL ?
|
||||
"stdio cpath rpath wpath exec proc dns inet" :
|
||||
"stdio cpath rpath wpath exec proc dns inet disklabel",
|
||||
NULL) == -1)
|
||||
fatal("pledge");
|
||||
|
||||
if ((sc = agent_init(argv[0], dryrun, timeout)) == NULL)
|
||||
if ((sc = agent_init(argv[0], rootdisk, dryrun, timeout)) == NULL)
|
||||
fatalx("agent");
|
||||
|
||||
/*
|
||||
* XXX Detect cloud with help from hostctl and sysctl
|
||||
* XXX in addition to the interface name.
|
||||
*/
|
||||
if (strcmp("hvn0", sc->sc_interface) == 0)
|
||||
ret = azure(sc);
|
||||
else if (strcmp("xnf0", sc->sc_interface) == 0)
|
||||
ret = ec2(sc);
|
||||
else
|
||||
ret = openstack(sc);
|
||||
if (rootdisk != NULL && growdisk(sc) == -1)
|
||||
fatalx("failed to grow %s", rootdisk);
|
||||
|
||||
sc->sc_clouds = &clouds;
|
||||
sc->sc_args = args;
|
||||
if (username != NULL) {
|
||||
free(sc->sc_username);
|
||||
sc->sc_username = username;
|
||||
}
|
||||
|
||||
if (TAILQ_EMPTY(&clouds)) {
|
||||
/*
|
||||
* XXX Auto-detect cloud with help from hostctl and
|
||||
* XXX sysctl in addition to the interface name.
|
||||
*/
|
||||
if (strcmp("hvn0", sc->sc_interface) == 0) {
|
||||
cloud_add(AZURE, &clouds);
|
||||
} else if (strcmp("xnf0", sc->sc_interface) == 0) {
|
||||
cloud_add(OPENNEBULA, &clouds);
|
||||
cloud_add(EC2, &clouds);
|
||||
} else {
|
||||
cloud_add(OPENNEBULA, &clouds);
|
||||
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)
|
||||
log_debug("%s: %s", __func__, sc->sc_stack);
|
||||
|
||||
if (sc->sc_dryrun) {
|
||||
agent_free(sc);
|
||||
|
|
81
agent/main.h
81
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
|
||||
|
@ -29,12 +29,30 @@
|
|||
#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,
|
||||
LINE,
|
||||
TEXT
|
||||
};
|
||||
|
||||
enum state {
|
||||
STATE_INIT,
|
||||
STATE_DHCP,
|
||||
STATE_169,
|
||||
STATE_DONE
|
||||
};
|
||||
|
||||
struct ssh_pubkey {
|
||||
char *ssh_keyval;
|
||||
char *ssh_keyfp;
|
||||
|
@ -43,23 +61,66 @@ 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;
|
||||
|
@ -92,19 +153,29 @@ 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 *);
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Reyk Floeter <reyk@openbsd.org>
|
||||
* 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
|
||||
|
@ -34,17 +34,11 @@ static int openstack_fetch(struct system_config *);
|
|||
int
|
||||
openstack(struct system_config *sc)
|
||||
{
|
||||
if ((dhcp_getendpoint(sc) == -1) &&
|
||||
(sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
|
||||
log_warnx("failed to set defaults");
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
sc->sc_state = STATE_DHCP;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (openstack_fetch(sc) != 0) {
|
||||
free(sc->sc_endpoint);
|
||||
return (cloudinit(sc));
|
||||
}
|
||||
return (0);
|
||||
return openstack_fetch(sc);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -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