config runner via PAT. (#874)

This commit is contained in:
Tingluo Huang
2020-12-25 21:54:23 -05:00
committed by TingluoHuang
parent 2b83621809
commit a57f90280f
4 changed files with 119 additions and 5 deletions

View File

@@ -99,9 +99,11 @@ namespace GitHub.Runner.Common
// Secret args. Must be added to the "Secrets" getter as well. // Secret args. Must be added to the "Secrets" getter as well.
public static readonly string Token = "token"; public static readonly string Token = "token";
public static readonly string PAT = "pat";
public static readonly string WindowsLogonPassword = "windowslogonpassword"; public static readonly string WindowsLogonPassword = "windowslogonpassword";
public static string[] Secrets => new[] public static string[] Secrets => new[]
{ {
PAT,
Token, Token,
WindowsLogonPassword, WindowsLogonPassword,
}; };

View File

@@ -42,6 +42,7 @@ namespace GitHub.Runner.Listener
Constants.Runner.CommandLine.Args.Labels, Constants.Runner.CommandLine.Args.Labels,
Constants.Runner.CommandLine.Args.MonitorSocketAddress, Constants.Runner.CommandLine.Args.MonitorSocketAddress,
Constants.Runner.CommandLine.Args.Name, Constants.Runner.CommandLine.Args.Name,
Constants.Runner.CommandLine.Args.PAT,
Constants.Runner.CommandLine.Args.RunnerGroup, Constants.Runner.CommandLine.Args.RunnerGroup,
Constants.Runner.CommandLine.Args.StartupType, Constants.Runner.CommandLine.Args.StartupType,
Constants.Runner.CommandLine.Args.Token, Constants.Runner.CommandLine.Args.Token,
@@ -187,6 +188,11 @@ namespace GitHub.Runner.Listener
validator: Validators.NonEmptyValidator); validator: Validators.NonEmptyValidator);
} }
public string GetGitHubPersonalAccessToken()
{
return GetArg(name: Constants.Runner.CommandLine.Args.PAT);
}
public string GetRunnerRegisterToken() public string GetRunnerRegisterToken()
{ {
return GetArgOrPrompt( return GetArgOrPrompt(

View File

@@ -4,7 +4,6 @@ using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Services.Common; using GitHub.Services.Common;
using GitHub.Services.OAuth; using GitHub.Services.OAuth;
using GitHub.Services.WebApi;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -12,6 +11,7 @@ using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace GitHub.Runner.Listener.Configuration namespace GitHub.Runner.Listener.Configuration
@@ -107,8 +107,8 @@ namespace GitHub.Runner.Listener.Configuration
else else
{ {
runnerSettings.GitHubUrl = inputUrl; runnerSettings.GitHubUrl = inputUrl;
var githubToken = command.GetRunnerRegisterToken(); var registerToken = await GetRunnerTokenAsync(command, inputUrl, "registration");
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken, Constants.RunnerEvent.Register); GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
runnerSettings.ServerUrl = authResult.TenantUrl; runnerSettings.ServerUrl = authResult.TenantUrl;
creds = authResult.ToVssCredentials(); creds = authResult.ToVssCredentials();
Trace.Info("cred retrieved via GitHub auth"); Trace.Info("cred retrieved via GitHub auth");
@@ -374,8 +374,8 @@ namespace GitHub.Runner.Listener.Configuration
} }
else else
{ {
var githubToken = command.GetRunnerDeletionToken(); var deletionToken = await GetRunnerTokenAsync(command, settings.GitHubUrl, "remove");
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken, Constants.RunnerEvent.Remove); GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, deletionToken, Constants.RunnerEvent.Remove);
creds = authResult.ToVssCredentials(); creds = authResult.ToVssCredentials();
Trace.Info("cred retrieved via GitHub auth"); Trace.Info("cred retrieved via GitHub auth");
} }
@@ -516,6 +516,102 @@ namespace GitHub.Runner.Listener.Configuration
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase); string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase);
} }
private async Task<string> GetRunnerTokenAsync(CommandSettings command, string githubUrl, string tokenType)
{
var githubPAT = command.GetGitHubPersonalAccessToken();
var runnerToken = string.Empty;
if (!string.IsNullOrEmpty(githubPAT))
{
Trace.Info($"Retriving runner {tokenType} token using GitHub PAT.");
var jitToken = await GetJITRunnerTokenAsync(githubUrl, githubPAT, tokenType);
Trace.Info($"Retrived runner {tokenType} token is good to {jitToken.ExpiresAt}.");
HostContext.SecretMasker.AddValue(jitToken.Token);
runnerToken = jitToken.Token;
}
if (string.IsNullOrEmpty(runnerToken))
{
if (string.Equals("registration", tokenType, StringComparison.OrdinalIgnoreCase))
{
runnerToken = command.GetRunnerRegisterToken();
}
else
{
runnerToken = command.GetRunnerDeletionToken();
}
}
return runnerToken;
}
private async Task<GitHubRunnerRegisterToken> GetJITRunnerTokenAsync(string githubUrl, string githubToken, string tokenType)
{
var githubApiUrl = "";
var gitHubUrlBuilder = new UriBuilder(githubUrl);
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
if (path.Length == 1)
{
// org runner
if (IsHostedServer(gitHubUrlBuilder))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/orgs/{path[0]}/actions/runners/{tokenType}-token";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runners/{tokenType}-token";
}
}
else if (path.Length == 2)
{
// repo or enterprise runner.
var repoScope = "repos/";
if (string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase))
{
repoScope = "";
}
if (IsHostedServer(gitHubUrlBuilder))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{repoScope}{path[0]}/{path[1]}/actions/runners/{tokenType}-token";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{repoScope}{path[0]}/{path[1]}/actions/runners/{tokenType}-token";
}
}
else
{
throw new ArgumentException($"'{githubUrl}' should point to an org or repository.");
}
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
using (var httpClient = new HttpClient(httpClientHandler))
{
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"github:{githubToken}"));
HostContext.SecretMasker.AddValue(base64EncodingToken);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("basic", base64EncodingToken);
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github.v3+json");
var response = await httpClient.PostAsync(githubApiUrl, new StringContent(string.Empty));
if (response.IsSuccessStatusCode)
{
Trace.Info($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
var jsonResponse = await response.Content.ReadAsStringAsync();
return StringUtil.ConvertFromJson<GitHubRunnerRegisterToken>(jsonResponse);
}
else
{
_term.WriteError($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
var errorResponse = await response.Content.ReadAsStringAsync();
_term.WriteError(errorResponse);
response.EnsureSuccessStatusCode();
return null;
}
}
}
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent) private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent)
{ {
var githubApiUrl = ""; var githubApiUrl = "";

View File

@@ -71,6 +71,16 @@ namespace GitHub.Runner.Listener.Configuration
} }
} }
[DataContract]
public sealed class GitHubRunnerRegisterToken
{
[DataMember(Name = "token")]
public string Token { get; set; }
[DataMember(Name = "expires_at")]
public string ExpiresAt { get; set; }
}
[DataContract] [DataContract]
public sealed class GitHubAuthResult public sealed class GitHubAuthResult
{ {