Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 14f6f9a

Browse files
authored
Merge branch 'main' into file-formats
2 parents 280cc29 + 349604f commit 14f6f9a

File tree

18 files changed

+159
-272
lines changed

18 files changed

+159
-272
lines changed

docs/unmnaged-nodes.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Unmanaged Nodes
2+
The default mode of OneFuzz is to run the agents inside scalesets managed by the the Onefuzz instance. But it is possible to run outside of the Instance infrastructure.
3+
This is the unmanaged scenario. In this mode, the user can use their own resource to participate in the fuzzing.
4+
5+
## Set-up
6+
These are the steps to run an unmanaged node
7+
8+
9+
### Create an Application Registration in Azure Active Directory
10+
We will create the authentication method for the unmanaged node.
11+
From the [azure cli](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) create a new **application registration**:
12+
```cmd
13+
az ad app create --display-name <registration_name>
14+
```
15+
Then use the application `app_id` in the result to create the associated **service principal**:
16+
17+
```cmd
18+
az ad sp create --id <app_id>
19+
```
20+
Take note of the `id` returned by this request. We will call it the `principal_id`.
21+
22+
Next, create a `client_secret`:
23+
24+
```
25+
az ad app credential reset --id <pp_id> --append
26+
```
27+
Take note of the `password` returned.
28+
29+
### Authorize the application in OneFuzz
30+
From the OneFuzz `deployment` folder run the following script using the `app_id` from above:
31+
``` cmd
32+
python .\deploylib\registration.py register_app <onefuzz_instance_id> <subscription_id> --app_id <app_id> --role UnmanagedNode
33+
```
34+
35+
### Create an unmanaged pool
36+
Using the OneFuzz CLI:
37+
``` cmd
38+
onefuzz pools create <pool_name> <os> --unmanaged --object_id <principal_id>
39+
```
40+
41+
### Download the agent binaries and the agent configuration
42+
Download a zip file containing the agent binaries:
43+
```
44+
onefuzz tools get <destination_folder>
45+
```
46+
Extract the zip file in a folder of your choice.
47+
48+
Download the configuration file for the agent:
49+
50+
```
51+
onefuzz pools get_config <pool_name>
52+
```
53+
54+
Under the `client_credential` section of the agent config file, update `client_id` and `client_secret`:
55+
```json
56+
{
57+
"client_id": "<app_id>",
58+
"client_secret": "<password>",
59+
}
60+
```
61+
Save the config to the file.
62+
63+
### Start the agent.
64+
Navigate to the folder corresponding to your OS.
65+
Set the necessary environment variable by running the script `set-env.ps1` (for Windows) or `set-env.sh` (for Linux).
66+
Run the agent with the following command. If you need more nodes use a different `machine_guid` for each one:
67+
```cmd
68+
onefuzz-agent run --machine_id <machine_guid> -c <path_to_config_file> --reset_lock
69+
```
70+
71+
### Verify that the agent is registered to OneFuzz
72+
73+
Using the OneFuzz CLI run the following command:
74+
75+
```
76+
onefuzz nodes get <machine_guid>
77+
```
78+
79+
This should return one entry. Verify that the `pool_name` matched the pool name created earlier.
80+
From here you will be able to schedule jobs on that pool and they will be running.

src/ApiService/ApiService/Functions/AgentRegistration.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ private async Async.Task<HttpResponseData> Post(HttpRequestData req) {
152152
MachineId: machineId,
153153
ScalesetId: scalesetId,
154154
InstanceId: instanceId,
155-
Version: version
155+
Version: version,
156+
Os: os ?? pool.Os,
157+
Managed: pool.Managed
156158
);
157159

158160
var r = await _context.NodeOperations.Replace(node);

src/ApiService/ApiService/OneFuzzTypes/Model.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ public record Node
110110

111111
bool ReimageRequested = false,
112112
bool DeleteRequested = false,
113-
bool DebugKeepNode = false
113+
bool DebugKeepNode = false,
114+
bool Managed = true
114115
) : StatefulEntityBase<NodeState>(State) {
115116

116117
public List<NodeTasks>? Tasks { get; set; }

src/ApiService/ApiService/ServiceConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public interface IServiceConfig {
3232
public ResourceIdentifier? OneFuzzFuncStorage { get; }
3333
public string? OneFuzzInstance { get; }
3434
public string? OneFuzzInstanceName { get; }
35+
public string? OneFuzzEndpoint { get; }
3536
public string? OneFuzzKeyvault { get; }
3637

3738
public string? OneFuzzMonitor { get; }
@@ -117,6 +118,7 @@ public ResourceIdentifier? OneFuzzFuncStorage {
117118

118119
public string? OneFuzzInstance { get => GetEnv("ONEFUZZ_INSTANCE"); }
119120
public string? OneFuzzInstanceName { get => GetEnv("ONEFUZZ_INSTANCE_NAME"); }
121+
public string? OneFuzzEndpoint { get => GetEnv("ONEFUZZ_ENDPOINT"); }
120122
public string? OneFuzzKeyvault { get => GetEnv("ONEFUZZ_KEYVAULT"); }
121123
public string? OneFuzzMonitor { get => GetEnv("ONEFUZZ_MONITOR"); }
122124
public string? OneFuzzOwner { get => GetEnv("ONEFUZZ_OWNER"); }

src/ApiService/ApiService/onefuzzlib/Creds.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,10 @@ public Async.Task<Region> GetBaseRegion() {
105105
});
106106
}
107107

108-
public Uri GetInstanceUrl()
109-
=> new($"https://{GetInstanceName()}.azurewebsites.net");
108+
public Uri GetInstanceUrl() {
109+
var onefuzzEndpoint = _config.OneFuzzEndpoint;
110+
return onefuzzEndpoint != null ? new Uri(onefuzzEndpoint) : new($"https://{GetInstanceName()}.azurewebsites.net");
111+
}
110112

111113
public record ScaleSetIdentity(string principalId);
112114

src/ApiService/ApiService/onefuzzlib/NodeOperations.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,10 @@ public async Async.Task CleanupBusyNodesWithoutWork() {
337337
}
338338

339339
public async Async.Task<Node> ToReimage(Node node, bool done = false) {
340+
if (!node.Managed) {
341+
_logTracer.Info($"skip reimage for unmanaged node: {node.MachineId:Tag:MachineId}");
342+
return node;
343+
}
340344

341345
var nodeState = node.State;
342346
if (done) {

src/ApiService/IntegrationTests/Fakes/TestServiceConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public TestServiceConfiguration(string tablePrefix) {
3131

3232
// -- Remainder not implemented --
3333

34+
public string? OneFuzzEndpoint => throw new System.NotImplementedException();
35+
3436
public LogDestination[] LogDestinations { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
3537

3638
public SeverityLevel LogSeverityLevel => throw new System.NotImplementedException();

src/agent/onefuzz-agent/src/config.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ struct RawStaticConfig {
8080
}
8181

8282
impl StaticConfig {
83-
pub async fn new(data: &[u8]) -> Result<Self> {
83+
pub async fn new(data: &[u8], machine_identity: Option<MachineIdentity>) -> Result<Self> {
8484
let config: RawStaticConfig = serde_json::from_slice(data)?;
8585

8686
let credentials = match config.client_credentials {
@@ -104,7 +104,7 @@ impl StaticConfig {
104104
managed.into()
105105
}
106106
};
107-
let machine_identity = match config.machine_identity {
107+
let machine_identity = match machine_identity.or(config.machine_identity) {
108108
Some(machine_identity) => machine_identity,
109109
None => MachineIdentity::from_metadata().await?,
110110
};
@@ -125,11 +125,14 @@ impl StaticConfig {
125125
Ok(config)
126126
}
127127

128-
pub async fn from_file(config_path: impl AsRef<Path>) -> Result<Self> {
128+
pub async fn from_file(
129+
config_path: impl AsRef<Path>,
130+
machine_identity: Option<MachineIdentity>,
131+
) -> Result<Self> {
129132
let config_path = config_path.as_ref();
130133
let data = std::fs::read(config_path)
131134
.with_context(|| format!("unable to read config file: {}", config_path.display()))?;
132-
Self::new(&data).await
135+
Self::new(&data, machine_identity).await
133136
}
134137

135138
pub fn from_env() -> Result<Self> {

src/agent/onefuzz-agent/src/main.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,6 @@ fn run(opt: RunOpt) -> Result<()> {
170170
if opt.redirect_output.is_some() {
171171
return redirect(opt);
172172
}
173-
let opt_machine_id = opt.machine_id;
174-
let opt_machine_name = opt.machine_name.clone();
175173
let rt = tokio::runtime::Runtime::new()?;
176174
let reset_lock = opt.reset_node_lock;
177175
let config = rt.block_on(load_config(opt));
@@ -184,15 +182,6 @@ fn run(opt: RunOpt) -> Result<()> {
184182

185183
let config = config?;
186184

187-
let config = StaticConfig {
188-
machine_identity: MachineIdentity {
189-
machine_id: opt_machine_id.unwrap_or(config.machine_identity.machine_id),
190-
machine_name: opt_machine_name.unwrap_or(config.machine_identity.machine_name),
191-
..config.machine_identity
192-
},
193-
..config
194-
};
195-
196185
if reset_lock {
197186
done::remove_done_lock(config.machine_identity.machine_id)?;
198187
} else if done::is_agent_done(config.machine_identity.machine_id)? {
@@ -218,10 +207,18 @@ fn run(opt: RunOpt) -> Result<()> {
218207
}
219208

220209
async fn load_config(opt: RunOpt) -> Result<StaticConfig> {
221-
info!("loading supervisor agent config");
210+
info!("loading supervisor agent config: {:?}", opt);
211+
let opt_machine_id = opt.machine_id;
212+
let opt_machine_name = opt.machine_name.clone();
213+
214+
let machine_identity = opt_machine_id.map(|machine_id| MachineIdentity {
215+
machine_id,
216+
machine_name: opt_machine_name.unwrap_or(format!("{}", machine_id)),
217+
scaleset_name: None,
218+
});
222219

223220
let config = match &opt.config_path {
224-
Some(config_path) => StaticConfig::from_file(config_path).await?,
221+
Some(config_path) => StaticConfig::from_file(config_path, machine_identity).await?,
225222
None => StaticConfig::from_env()?,
226223
};
227224

src/agent/onefuzz/src/auth.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,11 @@ impl ClientCredentials {
113113
pub async fn access_token(&self) -> Result<AccessToken> {
114114
let (authority, scope) = {
115115
let url = Url::parse(&self.resource.clone())?;
116-
let host = url.host_str().ok_or_else(|| {
116+
let port = url.port().map(|p| format!(":{}", p)).unwrap_or_default();
117+
let host_name = url.host_str().ok_or_else(|| {
117118
anyhow::format_err!("resource URL does not have a host string: {}", url)
118119
})?;
120+
let host = format!("{}{}", host_name, port);
119121
if let Some(domain) = &self.multi_tenant_domain {
120122
let instance: Vec<&str> = host.split('.').collect();
121123
(

0 commit comments

Comments
 (0)