mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
set auto-mount service account token to false in keycloak pods (#40605)
closes #38843 Signed-off-by: AvivGuiser <avivguiser@gmail.com> Co-authored-by: Steven Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
5ad1b1efa4
commit
3c8af6dec5
@ -377,6 +377,8 @@ stringData:
|
||||
|
||||
When running on a Kubernetes or OpenShift environment well-known locations of trusted certificates are included automatically.
|
||||
This includes `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` and the `/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt` when present.
|
||||
In order to not include `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` in Keycloak pods, set the `automountServiceAccountToken` field in the spec to `false`
|
||||
This is useful if some security policies require that the service account token is not mounted in the pod, but it cannot be `false` if you plan to use an external infinispan cluster, or if plan to use the Kubernetes service accounts identity provider, or if you have some custom provider logic which expects to implicitly use the Kubernetes API.
|
||||
|
||||
=== Admin Bootstrapping
|
||||
|
||||
|
||||
@ -327,6 +327,8 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
|
||||
if (!specBuilder.hasDnsPolicy()) {
|
||||
specBuilder.withDnsPolicy("ClusterFirst");
|
||||
}
|
||||
boolean automount = keycloakCR.getSpec().getAutomountServiceAccountToken();
|
||||
specBuilder.withAutomountServiceAccountToken(automount);
|
||||
handleScheduling(keycloakCR, schedulingLabels, specBuilder);
|
||||
|
||||
// there isn't currently an editOrNewFirstContainer, so we need to do this manually
|
||||
@ -463,15 +465,18 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
|
||||
LinkedHashMap<String, EnvVar> varMap = Stream.concat(Stream.concat(unsupportedEnv.stream(), firstClasssEnvVars.stream()), Stream.concat(additionalEnvVars.stream(), env))
|
||||
.collect(Collectors.toMap(EnvVar::getName, Function.identity(), (e1, e2) -> e1, LinkedHashMap::new));
|
||||
|
||||
String truststores = SERVICE_ACCOUNT_DIR + "ca.crt";
|
||||
|
||||
if (useServiceCaCrt) {
|
||||
truststores += "," + SERVICE_CA_CRT;
|
||||
if (!Boolean.FALSE.equals(keycloakCR.getSpec().getAutomountServiceAccountToken())) {
|
||||
String truststores = SERVICE_ACCOUNT_DIR + "ca.crt";
|
||||
|
||||
if (useServiceCaCrt) {
|
||||
truststores += "," + SERVICE_CA_CRT;
|
||||
}
|
||||
|
||||
// include the kube CA if the user is not controlling KC_TRUSTSTORE_PATHS via the unsupported or the additional
|
||||
varMap.putIfAbsent(KC_TRUSTSTORE_PATHS, new EnvVarBuilder().withName(KC_TRUSTSTORE_PATHS).withValue(truststores).build());
|
||||
}
|
||||
|
||||
// include the kube CA if the user is not controlling KC_TRUSTSTORE_PATHS via the unsupported or the additional
|
||||
varMap.putIfAbsent(KC_TRUSTSTORE_PATHS, new EnvVarBuilder().withName(KC_TRUSTSTORE_PATHS).withValue(truststores).build());
|
||||
|
||||
setTracingEnvVars(keycloakCR, varMap);
|
||||
|
||||
var envVars = new ArrayList<>(varMap.values());
|
||||
|
||||
@ -162,6 +162,10 @@ public class KeycloakSpec {
|
||||
@JsonPropertyDescription("Configuration related to the generated ServiceMonitor")
|
||||
private ServiceMonitorSpec serviceMonitorSpec;
|
||||
|
||||
@JsonProperty("automountServiceAccountToken")
|
||||
@JsonPropertyDescription("Set this to to false to disable automounting the default ServiceAccount Token and Service CA. This is enabled by default.")
|
||||
private boolean automountServiceAccountToken = true;
|
||||
|
||||
public HttpSpec getHttpSpec() {
|
||||
return httpSpec;
|
||||
}
|
||||
@ -386,4 +390,11 @@ public class KeycloakSpec {
|
||||
public void setServiceMonitorSpec(ServiceMonitorSpec serviceMonitorSpec) {
|
||||
this.serviceMonitorSpec = serviceMonitorSpec;
|
||||
}
|
||||
|
||||
public boolean getAutomountServiceAccountToken() {
|
||||
return automountServiceAccountToken;
|
||||
}
|
||||
public void setAutomountServiceAccountToken(boolean automountServiceAccountToken) {
|
||||
this.automountServiceAccountToken = automountServiceAccountToken;
|
||||
}
|
||||
}
|
||||
|
||||
@ -747,7 +747,16 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||
assertThat(limits).isNotNull();
|
||||
assertThat(limits.get("memory")).isEqualTo(config.keycloak().resources().limits().memory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoAutoMountServiceAccount() {
|
||||
var kc = getTestKeycloakDeployment(true);
|
||||
kc.getSpec().setAutomountServiceAccountToken(Boolean.FALSE);
|
||||
deployKeycloak(k8sclient, kc, true);
|
||||
var pods = k8sclient.pods().inNamespace(namespace).withLabels(Constants.DEFAULT_LABELS).list().getItems();
|
||||
assertThat(pods).isNotNull();
|
||||
assertThat(pods).isNotEmpty();
|
||||
assertThat(pods.get(0).getSpec().getAutomountServiceAccountToken()).isEqualTo(Boolean.FALSE);
|
||||
}
|
||||
private void handleFakeImagePullSecretCreation(Keycloak keycloakCR,
|
||||
String secretDescriptorFilename) {
|
||||
|
||||
|
||||
@ -336,5 +336,11 @@ public class CRSerializationTest {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoAutoMountServiceAccountToken() {
|
||||
var keycloak = Serialization.unmarshal(this.getClass().getResourceAsStream("/test-serialization-keycloak-cr-without-automount.yml"), Keycloak.class);
|
||||
var keycloakSpec = keycloak.getSpec();
|
||||
assertNotNull(keycloakSpec);
|
||||
assertFalse(keycloakSpec.getAutomountServiceAccountToken());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
apiVersion: k8s.keycloak.org/v2alpha1
|
||||
kind: Keycloak
|
||||
metadata:
|
||||
name: test-serialization-kc
|
||||
spec:
|
||||
instances: 3
|
||||
image: my-image
|
||||
automountServiceAccountToken: false
|
||||
additionalOptions:
|
||||
- name: key1
|
||||
value: value1
|
||||
- name: features
|
||||
value: docker
|
||||
db:
|
||||
vendor: vendor
|
||||
usernameSecret:
|
||||
name: usernameSecret
|
||||
key: usernameSecretKey
|
||||
passwordSecret:
|
||||
name: passwordSecret
|
||||
key: passwordSecretKey
|
||||
host: host
|
||||
database: database
|
||||
url: url
|
||||
port: 123
|
||||
schema: schema
|
||||
poolInitialSize: 1
|
||||
poolMinSize: 2
|
||||
poolMaxSize: 3
|
||||
ingress:
|
||||
enabled: false
|
||||
className: nginx
|
||||
annotations:
|
||||
myAnnotation: myValue
|
||||
anotherAnnotation: anotherValue
|
||||
readinessProbe:
|
||||
periodSeconds: 50
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
periodSeconds: 60
|
||||
failureThreshold: 1
|
||||
startupProbe:
|
||||
periodSeconds: 40
|
||||
failureThreshold: 2
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
http:
|
||||
- ipBlock:
|
||||
cidr: 172.17.0.0/16
|
||||
except:
|
||||
- 172.17.1.0/24
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
project: myproject
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
role: frontend
|
||||
https:
|
||||
- ipBlock:
|
||||
cidr: 172.17.0.0/16
|
||||
except:
|
||||
- 172.17.1.0/24
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
project: myproject
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
role: frontend
|
||||
management:
|
||||
- ipBlock:
|
||||
cidr: 172.17.0.0/16
|
||||
except:
|
||||
- 172.17.1.0/24
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
project: myproject
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
role: frontend
|
||||
http:
|
||||
httpEnabled: true
|
||||
httpPort: 123
|
||||
httpsPort: 456
|
||||
tlsSecret: my-tls-secret
|
||||
hostname:
|
||||
hostname: my-hostname
|
||||
admin: my-admin-hostname
|
||||
adminUrl: https://www.my-admin-hostname.org:8448/something
|
||||
strict: true
|
||||
strictBackchannel: true
|
||||
backchannelDynamic: true
|
||||
cache:
|
||||
configMapFile:
|
||||
name: my-config-map
|
||||
key: file.xml
|
||||
features:
|
||||
enabled:
|
||||
- docker
|
||||
- authorization
|
||||
disabled:
|
||||
- admin
|
||||
- step-up-authentication
|
||||
transaction:
|
||||
xaEnabled: false
|
||||
tracing:
|
||||
enabled: true
|
||||
endpoint: http://my-tracing:4317
|
||||
serviceName: my-best-keycloak
|
||||
protocol: http/protobuf
|
||||
samplerType: parentbased_traceidratio
|
||||
samplerRatio: 0.01
|
||||
compression: gzip
|
||||
resourceAttributes:
|
||||
service.namespace: keycloak-namespace
|
||||
service.name: custom-service-name
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "500M"
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: "1500M"
|
||||
proxy:
|
||||
headers: forwarded
|
||||
truststores:
|
||||
x:
|
||||
secret:
|
||||
name: my-secret
|
||||
httpManagement:
|
||||
port: 9003
|
||||
bootstrapAdmin:
|
||||
user:
|
||||
secret: something
|
||||
service:
|
||||
secret: else
|
||||
update:
|
||||
strategy: Auto
|
||||
revision: 1
|
||||
unsupported:
|
||||
podTemplate:
|
||||
metadata:
|
||||
labels:
|
||||
my-label: "foo"
|
||||
Loading…
x
Reference in New Issue
Block a user