Skip to content

Commit

Permalink
feat(provider/kubernetes): v2 restrict namespaces for uploading manif…
Browse files Browse the repository at this point in the history
…ests (#2410)
  • Loading branch information
gardleopard authored and lwander committed Mar 9, 2018
1 parent 6ed3bb0 commit 299d35d
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ public class KubernetesV2Credentials implements KubernetesCredentials {
private final Clock clock;
private final String accountName;
private final ObjectMapper mapper = new ObjectMapper();
@Getter
private final List<String> namespaces;
@Getter
private final List<String> omitNamespaces;
private final List<KubernetesKind> kinds;
private final List<KubernetesKind> omitKinds;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials;
import com.netflix.spinnaker.clouddriver.security.AccountCredentials;
import com.netflix.spinnaker.clouddriver.security.AccountCredentialsProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.Errors;

Expand All @@ -28,6 +29,7 @@
import java.util.Collections;
import java.util.List;

@Slf4j
public class KubernetesValidationUtil {
final private String context;
final private Errors errors;
Expand Down Expand Up @@ -77,11 +79,16 @@ public boolean validateNotEmpty(String attribute, String value) {
return true;
}

public boolean validateV2Credentials(AccountCredentialsProvider provider, String accountName) {
public boolean validateV2Credentials(AccountCredentialsProvider provider, String accountName, String namespace) {
log.info("Validating credentials for {} {}", accountName, namespace);
if (!validateNotEmpty("account", accountName)) {
return false;
}

if (!validateNotEmpty("namespace", namespace)) {
return false;
}

AccountCredentials credentials = provider.getCredentials(accountName);
if (credentials == null) {
reject("notFound", "account");
Expand All @@ -93,6 +100,25 @@ public boolean validateV2Credentials(AccountCredentialsProvider provider, String
return false;
}

if (!validateNamespace(namespace, (KubernetesV2Credentials)credentials.getCredentials())) {
return false;
}

return true;
}

protected boolean validateNamespace(String namespace, KubernetesV2Credentials credentials) {
final List<String> configuredNamespaces = credentials.getNamespaces();
if (configuredNamespaces != null && !configuredNamespaces.isEmpty() && !configuredNamespaces.contains(namespace)) {
reject("wrongNamespace", namespace);
return false;
}

final List<String> omitNamespaces = credentials.getOmitNamespaces();
if (omitNamespaces != null && omitNamespaces.contains(namespace)) {
reject("omittedNamespace", namespace);
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.netflix.spinnaker.clouddriver.deploy.DescriptionValidator;
import com.netflix.spinnaker.clouddriver.kubernetes.KubernetesOperation;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.KubernetesCoordinates;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesDeleteManifestDescription;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.validator.KubernetesValidationUtil;
import com.netflix.spinnaker.clouddriver.security.AccountCredentialsProvider;
Expand All @@ -40,8 +41,10 @@ public class KubernetesDeleteManifestValidator extends DescriptionValidator<Kube
@Override
public void validate(List priorDescriptions, KubernetesDeleteManifestDescription description, Errors errors) {
KubernetesValidationUtil util = new KubernetesValidationUtil("deleteKubernetesManifest", errors);
if (!util.validateV2Credentials(provider, description.getAccount())) {
return;
for (KubernetesCoordinates coordinate : description.getAllCoordinates()) {
if (!util.validateV2Credentials(provider, description.getAccount(), coordinate.getNamespace())) {
return;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.netflix.spinnaker.clouddriver.deploy.DescriptionValidator;
import com.netflix.spinnaker.clouddriver.kubernetes.KubernetesOperation;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesDeployManifestDescription;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesManifest;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.validator.KubernetesValidationUtil;
import com.netflix.spinnaker.clouddriver.security.AccountCredentialsProvider;
import com.netflix.spinnaker.clouddriver.security.ProviderVersion;
Expand All @@ -40,8 +41,10 @@ public class KubernetesDeployManifestValidator extends DescriptionValidator<Kube
@Override
public void validate(List priorDescriptions, KubernetesDeployManifestDescription description, Errors errors) {
KubernetesValidationUtil util = new KubernetesValidationUtil("deployKubernetesManifest", errors);
if (!util.validateV2Credentials(provider, description.getAccount())) {
return;
for (KubernetesManifest manifest:description.getManifests()) {
if (!util.validateV2Credentials(provider, description.getAccount(), manifest.getNamespace())) {
return;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class KubernetesPauseRolloutManifestValidator extends DescriptionValidato
@Override
public void validate(List priorDescriptions, KubernetesPauseRolloutManifestDescription description, Errors errors) {
KubernetesValidationUtil util = new KubernetesValidationUtil("pauseRolloutKubernetesManifest", errors);
if (!util.validateV2Credentials(provider, description.getAccount())) {
if (!util.validateV2Credentials(provider, description.getAccount(), description.getPointCoordinates().getNamespace())) {
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class KubernetesResumeRolloutManifestValidator extends DescriptionValidat
@Override
public void validate(List priorDescriptions, KubernetesResumeRolloutManifestDescription description, Errors errors) {
KubernetesValidationUtil util = new KubernetesValidationUtil("resumeRolloutKubernetesManifest", errors);
if (!util.validateV2Credentials(provider, description.getAccount())) {
if (!util.validateV2Credentials(provider, description.getAccount(), description.getPointCoordinates().getNamespace())) {
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class KubernetesScaleManifestValidator extends DescriptionValidator<Kuber
@Override
public void validate(List priorDescriptions, KubernetesScaleManifestDescription description, Errors errors) {
KubernetesValidationUtil util = new KubernetesValidationUtil("scaleKubernetesManifest", errors);
if (!util.validateV2Credentials(provider, description.getAccount())) {
if (!util.validateV2Credentials(provider, description.getAccount(), description.getPointCoordinates().getNamespace())) {
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class KubernetesUndoRolloutManifestValidator extends DescriptionValidator
@Override
public void validate(List priorDescriptions, KubernetesUndoRolloutManifestDescription description, Errors errors) {
KubernetesValidationUtil util = new KubernetesValidationUtil("undoRolloutKubernetesManifest", errors);
if (!util.validateV2Credentials(provider, description.getAccount())) {
if (!util.validateV2Credentials(provider, description.getAccount(), description.getPointCoordinates().getNamespace())) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class KubernetesResizeServerGroupValidator extends DescriptionValidator<K
@Override
public void validate(List priorDescriptions, KubernetesResizeServerGroupDescription description, Errors errors) {
KubernetesValidationUtil util = new KubernetesValidationUtil("deployKubernetesManifest", errors);
if (!util.validateV2Credentials(provider, description.getAccount())) {
if (!util.validateV2Credentials(provider, description.getAccount(), description.getCoordinates().getNamespace())) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2018 Schibsted ASA.
*
* 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.netflix.spinnaker.clouddriver.kubernetes.v2.validator

import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials
import com.netflix.spinnaker.clouddriver.security.AccountCredentials
import com.netflix.spinnaker.clouddriver.security.AccountCredentialsProvider
import org.springframework.validation.Errors
import spock.lang.Specification
import spock.lang.Unroll

class KubernetesValidationUtilSpec extends Specification {
@Unroll
void "wiring of namespace validation"() {
given:
Errors errors = Mock(Errors)
String kubernetesAccount = "testAccount"
def namespaces = ["test-namespace"]
def omitNamespaces = ["omit-namespace"]
AccountCredentials accountCredentials = Mock(AccountCredentials)
KubernetesV2Credentials credentials = Mock(KubernetesV2Credentials)
KubernetesValidationUtil kubernetesValidationUtil = new KubernetesValidationUtil("currentContext", errors);
AccountCredentialsProvider accountCredentialsProvider = Mock(AccountCredentialsProvider)

when:
def judgement = kubernetesValidationUtil.validateV2Credentials(accountCredentialsProvider, kubernetesAccount, testNamespace)

then:
accountCredentialsProvider.getCredentials(kubernetesAccount) >> accountCredentials
accountCredentials.getCredentials() >> credentials
credentials.getOmitNamespaces() >> omitNamespaces
credentials.namespaces >> namespaces
judgement == expectedResult

where:
testNamespace || expectedResult
"test-namespace" || true
"omit-namespace" || false
"unknown-namespace" || false
}

@Unroll
void "validation of namespaces"() {
given:
Errors errors = Mock(Errors)
KubernetesV2Credentials credentials = Mock(KubernetesV2Credentials)
KubernetesValidationUtil kubernetesValidationUtil = new KubernetesValidationUtil("currentContext", errors);

when:
def judgement = kubernetesValidationUtil.validateNamespace(testNamespace, credentials)

then:
credentials.getOmitNamespaces() >> omitNamespaces
credentials.namespaces >> namespaces
judgement == allowedNamespace

where:
namespaces | omitNamespaces | testNamespace || allowedNamespace
["test-namespace"] | ["omit-namespace"] | "test-namespace" || true
null | ["omit-namespace"] | "test-namespace" || true
["test-namespace"] | null | "test-namespace" || true
["test-namespace"] | ["omit-namespace"] | "omit-namespace" || false
null | ["omit-namespace"] | "omit-namespace" || false
["test-namespace"] | ["omit-namespace"] | "unknown-namespace" || false
null | null | "unknown-namespace" || true
[] | [] | "unknown-namespace" || true
}
}

0 comments on commit 299d35d

Please sign in to comment.