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

Systemd start script: allow * from JAVA_OPTS to be passed to the Jenkins process (escaping in eval) #303

Open
tholewebgods opened this issue Apr 1, 2022 · 2 comments

Comments

@tholewebgods
Copy link

What feature do you want to see added?

The Jenkins start script accidentally expands * (to all files in the working directory) originating from an environment variable with its eval.

This lead to a 500 error, when opening a file from the directory browser:

Error while serving https://qa-hidrive.de/job/XXXXX/XXXXX/Report.html
org.eclipse.jetty.http.BadMessageException: 500: Response header too large
    at org.eclipse.jetty.server.HttpConnection$SendCallback.process(HttpConnection.java:768)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
    at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:223)
    at org.eclipse.jetty.server.HttpConnection.send(HttpConnection.java:550)
    at org.eclipse.jetty.server.HttpChannel.sendResponse(HttpChannel.java:910)
    at org.eclipse.jetty.server.HttpChannel.write(HttpChannel.java:987)
    at org.eclipse.jetty.server.HttpOutput.channelWrite(HttpOutput.java:285)
    at org.eclipse.jetty.server.HttpOutput.channelWrite(HttpOutput.java:269)
    at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:869)
    at com.jcraft.jzlib.DeflaterOutputStream.deflate(DeflaterOutputStream.java:144)
    at com.jcraft.jzlib.DeflaterOutputStream.write(DeflaterOutputStream.java:102)
    at org.kohsuke.stapler.compression.FilterServletOutputStream.write(FilterServletOutputStream.java:41)
    at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextServletOutputStream.write(OnCommittedResponseWrapper.java:638)
    at org.kohsuke.stapler.Stapler.serveStaticResource(Stapler.java:628)
    at org.kohsuke.stapler.ResponseImpl.serveFile(ResponseImpl.java:231)
    at hudson.model.DirectoryBrowserSupport.serveFile(DirectoryBrowserSupport.java:415)
    at hudson.model.DirectoryBrowserSupport.generateResponse(DirectoryBrowserSupport.java:164)
    at htmlpublisher.HtmlPublisherTarget$BaseHTMLAction.doDynamic(HtmlPublisherTarget.java:275)
    at htmlpublisher.HtmlPublisherTarget$HTMLAction.doDynamic(HtmlPublisherTarget.java:283)
    at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:710)
    at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:398)
Caused: java.lang.reflect.InvocationTargetException
    at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:402)
    at org.kohsuke.stapler.Function$InstanceFunction.invoke(Function.java:410)
    ...

This is what we had in the config initially:

cat /etc/systemd/system/jenkins.service.d/override.conf 
[Service]
Environment="JAVA_OPTS=-Djava.awt.headless=true \"-Dhudson.model.DirectoryBrowserSupport.CSP=sandbox allow-same-origin allow-scripts; default-src 'self'; script-src * 'unsafe-eval'; img-src *; style-src * 'unsafe-inline'; font-src *;\""

This is what the process command looked like:

jenkins    22175       1 99 17:32 ?        00:00:16 /usr/bin/java -Djava.awt.headless=true -Dhudson.model.DirectoryBrowserSupport.CSP=sandbox allow-same-origin allow-scripts; default-src 'self'; script-src Report browserstack-id.txt caches com.browserstack.automate.ci.jenkins.BrowserStackBuildWrapper.xml <<<... OMITTED A LOT OF OTHER SYSTEM FILES ...>>> workflow-libs workspace 'unsafe-eval'; img-src *; style-src Report browserstack-id.txt caches com.browserstack.automate.ci.jenkins.BrowserStackBuildWrapper.xml <<<... OMITTED A LOT OF OTHER SYSTEM FILES ...>>> workflow-libs workspace 'unsafe-inline'; font-src *; -jar /usr/share/java/jenkins.war --webroot=/var/cache/jenkins/war --httpPort=8080 --httpListenAddress=127.0.0.1

Initially I did not recognized this as an error until I compared that with the command, when the CSP config was absent:

jenkins    28509       1  8 09:26 ?        00:00:59 /usr/bin/java -Djava.awt.headless=true -jar /usr/share/java/jenkins.war --webroot=/var/cache/jenkins/war --httpPort=8080 --httpListenAddress=127.0.0.1

Now I understood * was expanded by the shell, so the CSP system property no longer contained the * but a lot of filenames from the working directory.

This is a MCVE for the underlying problem:

$ foo="\"a * b\"" ; eval echo ${foo}
a file-1.txt file-2 b
$ foo="\"a \* b\"" ; eval echo ${foo}
a \* b
$ foo="a \* b" ; eval echo ${foo}
a * b

$ foo="a * b" ; eval echo "${foo}"
a file-1.txt file-2 b
$ foo="\"a * b\"" ; eval echo "${foo}"
a * b

Although we probably stick with enumerating the actual domains instead of using *, I though you want to fix this.

This is a proposed fix:

--- /usr/bin/jenkins    2022-04-01 13:17:00.122267704 +0200
+++ /usr/bin/jenkins    2022-04-01 13:21:42.354954818 +0200
@@ -148,7 +148,7 @@
    unset JENKINS_WEBROOT
    eval exec \
        "${java_cmd}" \
-       ${java_opts_tmp} \
+       "${java_opts_tmp}" \
        -jar "${jenkins_war_tmp}" \
        ${inferred_jenkins_opts}
 }

This is the current Jenkins version:

$ sudo dpkg --list | grep 'jenkins'
ii  jenkins                               2.332.1                            all          Jenkins is an ...

Upstream changes

None.

@tholewebgods
Copy link
Author

Side note: we opted to enumerate domains instead of using *, but the underlying bug should be fixed anyway, IMO.

@timja
Copy link
Member

timja commented Apr 1, 2022

Are you aware that the above is only an escape hatch and the recommended approach is to configure a resource root url?
https://www.jenkins.io/doc/book/security/user-content/#resource-root-url

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants