in sources/Google.Solutions.IapDesktop.Extensions.Session/Protocol/Ssh/MetadataAuthorizedPublicKeyProcessor.cs [321:470]
public async Task<PlatformCredential> AuthorizeKeyAsync(
IAsymmetricKeySigner key,
TimeSpan validity,
string username,
KeyAuthorizationMethods allowedMethods,
IAuthorization authorization,
CancellationToken cancellationToken)
{
key.ExpectNotNull(nameof(key));
username.ExpectNotNull(nameof(username));
Debug.Assert(!this.IsOsLoginEnabled);
var instanceMetadata = this.instanceDetails.Metadata;
var projectMetadata = this.projectDetails.CommonInstanceMetadata;
//
// Check if there is a legacy SSH key. If there is one,
// other keys are ignored.
//
// NB. legacy SSH keys were instance-only, so checking
// the instance metadata is sufficient.
//
if (this.IsLegacySshKeyPresent)
{
throw new UnsupportedLegacySshKeyEncounteredException(
$"Connecting to the VM instance {this.instance.Name} is not supported " +
"because the instance uses legacy SSH keys in its metadata (sshKeys)",
HelpTopics.ManagingMetadataAuthorizedKeys);
}
//
// There is no legacy key, so we're good to push a new key.
//
// Now figure out which username to use and where to push it.
//
var blockProjectSshKeys = this.AreProjectSshKeysBlocked;
bool useInstanceKeySet;
if (allowedMethods.HasFlag(KeyAuthorizationMethods.ProjectMetadata) &&
allowedMethods.HasFlag(KeyAuthorizationMethods.InstanceMetadata))
{
//
// Both allowed - use project metadata unless:
// - project keys are blocked
// - we do not have the permission to update project metadata.
//
var canUpdateProjectMetadata = await this.resourceManagerAdapter
.IsAccessGrantedAsync(
this.instance.Project,
new[] {
Permissions.ComputeProjectsSetCommonInstanceMetadata,
Permissions.ServiceAccountsActAs
},
cancellationToken)
.ConfigureAwait(false);
useInstanceKeySet = blockProjectSshKeys || !canUpdateProjectMetadata;
}
else if (allowedMethods.HasFlag(KeyAuthorizationMethods.ProjectMetadata))
{
// Only project allowed.
if (blockProjectSshKeys)
{
throw new InvalidOperationException(
$"Project {this.instance.ProjectId} does not allow project-level SSH keys");
}
else
{
useInstanceKeySet = false;
}
}
else if (allowedMethods.HasFlag(KeyAuthorizationMethods.InstanceMetadata))
{
// Only instance allowed.
useInstanceKeySet = true;
}
else
{
// Neither project nor instance allowed.
throw new ArgumentException(nameof(allowedMethods));
}
var metadataKey = new ManagedMetadataAuthorizedPublicKey(
username,
key.PublicKey.Type,
Convert.ToBase64String(key.PublicKey.WireFormatValue),
new ManagedMetadataAuthorizedPublicKey.PublicKeyMetadata(
authorization.Session.Username,
DateTime.UtcNow.Add(validity)));
var existingKeySet = MetadataAuthorizedPublicKeySet.FromMetadata(
useInstanceKeySet
? instanceMetadata
: projectMetadata);
if (existingKeySet
.RemoveExpiredKeys()
.Contains(metadataKey))
{
//
// The key is there already, so we are all set.
//
ApplicationTraceSource.Log.TraceVerbose(
"Existing SSH key found for {0}",
username);
}
else
{
//
// Key not known yet, so we have to push it to
// the metadata.
//
ApplicationTraceSource.Log.TraceVerbose(
"Pushing new SSH key for {0}",
username);
await ModifyMetadataAndHandleErrorsAsync(
async token =>
{
if (useInstanceKeySet)
{
await this.computeClient
.UpdateMetadataAsync(
this.instance,
metadata => AddPublicKeyToMetadata(metadata, metadataKey),
token)
.ConfigureAwait(false);
}
else
{
await this.computeClient
.UpdateCommonInstanceMetadataAsync(
this.instance.Project,
metadata => AddPublicKeyToMetadata(metadata, metadataKey),
token)
.ConfigureAwait(false);
}
},
cancellationToken)
.ConfigureAwait(false);
}
return new PlatformCredential(
key,
useInstanceKeySet
? KeyAuthorizationMethods.InstanceMetadata
: KeyAuthorizationMethods.ProjectMetadata,
username);
}