-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAzureVMmetadata.R
147 lines (122 loc) · 4.76 KB
/
AzureVMmetadata.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
metadata_host <- httr::parse_url("http://169.254.169.254")
inst_api_version <- "2019-02-01"
att_api_version <- "2018-10-01"
ev_api_version <- "2017-11-01"
#' Metadata for an Azure VM
#'
#' @param nonce For `update_attested_metadata`, an optional string to use as a nonce.
#' @details
#' The `instance`, `attested` and `events` environments contain the instance metadata, attested metadata, and scheduled events respectively for a VM running in Azure. `instance` and `attested` are automatically populated when you load the AzureVMmetadata package, or you can manually populate them yourself with the `update_instance_metadata` and `update_attested_metadata` functions. `events` is not populated at package startup, because calling the scheduled event service can require up to several minutes if it is not running already. You can manually populate it with the `update_scheduled_events` function.
#'
#' If AzureVMmetadata is loaded in an R session that is _not_ running in an Azure VM, all the metadata environments will be empty.
#'
#' @return
#' The updating functions return the contents of their respective environments as lists, invisibly.
#' @format
#' `instance`, `attested` and `events` are environments.
#' @seealso
#' [in_azure_vm]
#'
#' [Instance metadata service documentation](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service)
#'
#' To obtain OAuth tokens from the metadata service, see [AzureAuth::get_managed_token]
#'
#' @examples
#'
#' ## these will only be meaningful when run in an Azure VM
#'
#' # all compute metadata
#' AzureVMmetadata::instance$compute
#'
#' # VM name and ID
#' AzureVMmetadata::instance$compute$name
#' AzureVMmetadata::instance$compute$vmId
#'
#' # VM resource details: subscription, resource group, resource ID
#' AzureVMmetadata::instance$compute$subscriptionId
#' AzureVMmetadata::instance$compute$resourceGroupName
#' AzureVMmetadata::instance$compute$resourceId
#'
#' # all network metadata
#' AzureVMmetadata::instance$network
#'
#' # IPv4 address details (1st network interface)
#' AzureVMmetadata::instance$network$interface[[1]]$ipv4
#'
#' @rdname metadata
#' @export
instance <- new.env()
#' @rdname metadata
#' @export
attested <- new.env()
#' @rdname metadata
#' @export
events <- new.env()
#' @rdname metadata
#' @export
update_instance_metadata <- function()
{
metadata_host$path <- "metadata/instance"
metadata_host$query <- list(`api-version`=att_api_version)
res <- try(httr::GET(metadata_host, httr::add_headers(metadata=TRUE)), silent=TRUE)
if(!inherits(res, "response") || res$status_code > 299)
return(invisible(NULL))
inst <- httr::content(res)
for(x in names(inst))
instance[[x]] <- inst[[x]]
invisible(inst)
}
#' @rdname metadata
#' @export
update_attested_metadata <- function(nonce=NULL)
{
metadata_host$path <- "metadata/attested/document"
metadata_host$query <- list(`api-version`=att_api_version, nonce=nonce)
res <- try(httr::GET(metadata_host, httr::add_headers(metadata=TRUE)), silent=TRUE)
if(!inherits(res, "response") || res$status_code > 299)
return(invisible(NULL))
att <- httr::content(res)
for(x in names(att))
attested[[x]] <- att[[x]]
invisible(att)
}
#' @rdname metadata
#' @export
update_scheduled_events <- function()
{
metadata_host$path <- "metadata/scheduledevents"
metadata_host$query <- list(`api-version`=ev_api_version)
res <- try(httr::GET(metadata_host, httr::add_headers(metadata=TRUE)), silent=TRUE)
if(!inherits(res, "response") || res$status_code > 299)
return(invisible(NULL))
ev <- httr::content(res)
for(x in names(ev))
events[[x]] <- ev[[x]]
invisible(ev)
}
#' Check if R is running in an Azure VM
#' @param nonce An optional string to use as a nonce.
#' @details
#' These functions check if R is running in an Azure VM by attempting to contact the instance metadata host. `in_azure_vm` simply returns TRUE or FALSE based on whether it succeeds. `get_vm_cert` provides a stronger check, by retrieving the VM's certificate and throwing an error if this is not found. Note that you should still verify the certificate's authenticity before relying on it.
#' @return
#' For `in_azure_vm`, a boolean. For `get_vm_cert`, a PKCS-7 certificate object.
#' @export
in_azure_vm <- function()
{
obj <- try(httr::GET(metadata_host), silent=TRUE)
inherits(obj, "response") && httr::status_code(obj) == 400
}
#' @rdname in_azure_vm
#' @export
get_vm_cert <- function(nonce=NULL)
{
update_attested_metadata(nonce)
if(is.null(attested$signature))
stop("No certificate found", call.=FALSE)
openssl::read_p7b(openssl::base64_decode(attested$signature))[[1]]
}
.onLoad <- function(libname, pkgname)
{
update_instance_metadata()
update_attested_metadata()
}