Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated ssh signer interface to include instance id #533

Merged
merged 1 commit into from
Aug 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ public interface SSHSigner {
* for a given principal
* @param principal Principal requesting the ssh certificates
* @param certRequest SSH Certificate Request
* @param instanceId Instance ID of the origin host
* @return SSH Certificates
*/
default SSHCertificates generateCertificate(Principal principal, SSHCertRequest certRequest) {
default SSHCertificates generateCertificate(Principal principal, SSHCertRequest certRequest,
final String instanceId) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public void testSSHSigner() {
SSHCertRequest certRequest = new SSHCertRequest();
Principal principal = Mockito.mock(Principal.class);
SSHCertificates certs = new SSHCertificates();
Mockito.when(signer.generateCertificate(principal, certRequest)).thenReturn(certs);
Mockito.when(signer.generateCertificate(principal, certRequest, "id")).thenReturn(certs);
Mockito.when(signer.getSignerCertificate("user")).thenReturn("ssh-cert");

SSHSignerFactory factory = () -> signer;

SSHSigner testSigner = factory.create();
assertNotNull(testSigner);

assertEquals(certs, testSigner.generateCertificate(principal, certRequest));
assertEquals(certs, testSigner.generateCertificate(principal, certRequest, "id"));
assertEquals("ssh-cert", testSigner.getSignerCertificate("user"));
testSigner.close();
}
Expand Down
9 changes: 8 additions & 1 deletion servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import com.yahoo.athenz.zts.cert.InstanceCertManager;
import com.yahoo.athenz.zts.cert.X509CertRecord;
import com.yahoo.athenz.zts.cert.X509CertRequest;
import com.yahoo.athenz.zts.cert.impl.X509CertUtils;
import com.yahoo.athenz.zts.store.ChangeLogStore;
import com.yahoo.athenz.zts.store.ChangeLogStoreFactory;
import com.yahoo.athenz.zts.store.CloudStore;
Expand Down Expand Up @@ -2569,7 +2570,13 @@ public SSHCertificates postSSHCertRequest(ResourceContext ctx, SSHCertRequest ce
// generate our ssh certificate

final Principal principal = ((RsrcCtxWrapper) ctx).principal();
SSHCertificates certs = instanceCertManager.getSSHCertificates(principal, certRequest);

// if we have a certificate then we'll try to extract
// the instance id for our request

final String instanceId = X509CertUtils.extractRequestInstanceId(principal.getX509Certificate());
SSHCertificates certs = instanceCertManager.getSSHCertificates(principal,
certRequest, instanceId);

metric.stopTiming(timerMetric);
return certs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void shutdown() {
}
}

void loadCertSigner() {
private void loadCertSigner() {

String certSignerFactoryClass = System.getProperty(ZTSConsts.ZTS_PROP_CERT_SIGNER_FACTORY_CLASS,
ZTSConsts.ZTS_CERT_SIGNER_FACTORY_CLASS);
Expand All @@ -123,7 +123,7 @@ void loadCertSigner() {
certSigner = certSignerFactory.create();
}

void loadSSHSigner(Authorizer authorizer) {
private void loadSSHSigner(Authorizer authorizer) {

String sshSignerFactoryClass = System.getProperty(ZTSConsts.ZTS_PROP_SSH_SIGNER_FACTORY_CLASS);
if (sshSignerFactoryClass == null || sshSignerFactoryClass.isEmpty()) {
Expand Down Expand Up @@ -398,7 +398,8 @@ public String getX509CertificateSigner() {
return caX509CertificateSigner;
}

public SSHCertificates getSSHCertificates(Principal principal, SSHCertRequest certRequest) {
public SSHCertificates getSSHCertificates(Principal principal, SSHCertRequest certRequest,
final String instanceId) {

if (sshSigner == null) {
LOGGER.error("getSSHCertificates: SSHSigner not available");
Expand All @@ -409,7 +410,7 @@ public SSHCertificates getSSHCertificates(Principal principal, SSHCertRequest ce
// of this request. the signer already was given the authorizer object
// that it can use for those checks.

return sshSigner.generateCertificate(principal, certRequest);
return sshSigner.generateCertificate(principal, certRequest, instanceId);
}

public boolean generateSshIdentity(InstanceIdentity identity, String sshCsr, String sshCertType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ public PKCS10CertificationRequest getCertReq() {
public void setCertReq(PKCS10CertificationRequest certReq) {
this.certReq = certReq;
}

public boolean parseCertRequest(StringBuilder errorMsg) {

// first we need to determine our instance id and dns suffix

for (String dnsName : dnsNames) {
int idx = dnsName.indexOf(ZTSConsts.ZTS_CERT_INSTANCE_ID);
if (instanceId == null && idx != -1) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2018 Oath Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yahoo.athenz.zts.cert.impl;

import com.yahoo.athenz.auth.util.Crypto;
import com.yahoo.athenz.zts.ZTSConsts;

import java.security.cert.X509Certificate;
import java.util.List;

public class X509CertUtils {

public static String extractRequestInstanceId(X509Certificate cert) {

if (cert == null) {
return null;
}

List<String> dnsNames = Crypto.extractX509CertDnsNames(cert);
for (String dnsName : dnsNames) {
int idx = dnsName.indexOf(ZTSConsts.ZTS_CERT_INSTANCE_ID);
if (idx != -1) {
return dnsName.substring(0, idx);
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -481,17 +481,17 @@ public void testGetSSHCertificates() {
InstanceCertManager instanceCertManager = new InstanceCertManager(null, null, true);
instanceCertManager.setSSHSigner(null);

assertNull(instanceCertManager.getSSHCertificates(null, null));
assertNull(instanceCertManager.getSSHCertificates(null, null, "id"));

SSHSigner signer = Mockito.mock(SSHSigner.class);

Principal principal = Mockito.mock(Principal.class);
SSHCertRequest certRequest = new SSHCertRequest();
com.yahoo.athenz.zts.SSHCertificates certs = new com.yahoo.athenz.zts.SSHCertificates();
Mockito.when(signer.generateCertificate(principal, certRequest)).thenReturn(certs);
Mockito.when(signer.generateCertificate(principal, certRequest, "id")).thenReturn(certs);
instanceCertManager.setSSHSigner(signer);

assertEquals(certs, instanceCertManager.getSSHCertificates(principal, certRequest));
assertEquals(certs, instanceCertManager.getSSHCertificates(principal, certRequest, "id"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2018 Oath Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yahoo.athenz.zts.cert.impl;

import static org.testng.Assert.*;

import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Collection;

import org.testng.annotations.Test;
import org.mockito.Mockito;

import java.security.cert.X509Certificate;

public class X509CertUtilsTest {

@Test
public void testExtractRequestInstanceId() throws CertificateParsingException {

assertNull(X509CertUtils.extractRequestInstanceId(null));

X509Certificate cert = Mockito.mock(X509Certificate.class);
Collection<List<?>> dnsNames = new ArrayList<>();
ArrayList<Object> item1 = new ArrayList<>();
item1.add(2);
item1.add("host1.domain.athenz");
dnsNames.add(item1);
Mockito.when(cert.getSubjectAlternativeNames()).thenReturn(dnsNames);

assertNull(X509CertUtils.extractRequestInstanceId(cert));

ArrayList<Object> item2 = new ArrayList<>();
item2.add(2);
item2.add("instanceid1.instanceid.athenz.test");
dnsNames.add(item2);

assertEquals("instanceid1", X509CertUtils.extractRequestInstanceId(cert));
}
}