如何以 root 身份从嵌入式码头启动端口 80 上的码头?

How to start jetty on port 80 as root from embedded jetty?

我正在尝试以 root 身份启动 https 端口 443,然后使用嵌入式码头降级为非 root 用户。我经历了 https://www.eclipse.org/jetty/documentation/current/setting-port80-access.html#configuring-jetty-setuid-feature 但没有从 java 程序中得到任何解决方案。

这是嵌入式码头代码:

package com.jetty.startup;

import java.io.File;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext;

import org.eclipse.jetty.annotations.ServletContainerInitializersStarter;
import org.eclipse.jetty.apache.jsp.JettyJasperInitializer;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;

import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;

import org.apache.log4j.Logger;

import org.eclipse.jetty.setuid.*;

/**
 * Handles Webapp server's serviice
 * 
 * 
 */
class MyServer {
   private static Logger logger = Logger.getLogger(MyServer.class);

   private static Server server;
   private String jettyHome;

   /**
    * Creates an instance of {@link MyServer}
    * 
    * @param jettyHome
    *            jetty home path
    */
    public MyServer(String jettyHome) {
       this.jettyHome = jettyHome;
    }

    /**
     * Initializes Webapp server:
     * 
     */
    public Server init() throws Exception {
      server = new Server();

      int httpsPort = 443;

      String keyStoreFile = "/home/jetty/webserver/etc/keystore";
      String keyStorePassword = "secret";
      String keyManagerPassword = "secret";
      String trustStorePassword = "secret";

     SslContextFactory sslContextFactory = new SslContextFactory();
     sslContextFactory.setKeyStorePath(keyStoreFile);
     sslContextFactory.setKeyStoreType("JKS");
     sslContextFactory.setKeyStorePassword(keyStorePassword);
     sslContextFactory.setKeyManagerPassword(keyManagerPassword);

     SetUIDListener set = new SetUIDListener();
     set.setStartServerAsPrivileged(true);
     set.setUsername("jetty");
     set.setGroupname("jetty");
     set.setUmask(002);

     server.addLifeCycleListener(set);

     HttpConfiguration httpConfiguration = new HttpConfiguration();
     httpConfiguration.setSecurePort(httpsPort);
     httpConfiguration.setSecureScheme("https");
     httpConfiguration.addCustomizer(new SecureRequestCustomizer());

     ServerConnector serverConnector = new ServerConnector(server, 
                    new SslConnectionFactory(sslContextFactory, "http/1.1"),
                    new HttpConnectionFactory(httpConfiguration));
     serverConnector.setPort(httpsPort);

     server.setConnectors(new Connector[] { serverConnector });

     WebAppContext myContext = new WebAppContext();
     myContext.setContextPath("/myapp");
     myContext.setWar(jettyHome + "/webapps/myapp/");
     myContext.setDefaultsDescriptor(jettyHome + "/etc/webdefault.xml");
     File overrideFile = new File(jettyHome
            + "/webapps/myapp/WEB-INF/generated-web.xml");
     if (overrideFile.exists()) {
        myContext.setOverrideDescriptor(jettyHome
                + "/webapps/myapp/WEB-INF/generated-web.xml");
     }

     server.setHandler(myContext);

     JettyJasperInitializer sci = new JettyJasperInitializer();
     ServletContainerInitializersStarter sciStarter = 
        new ServletContainerInitializersStarter(myContext);
     ContainerInitializer initializer = new ContainerInitializer(sci, null);
     List<ContainerInitializer> initializers = new ArrayList<>();
     initializers.add(initializer);

     myContext.setAttribute("org.eclipse.jetty.containerInitializers", initializers);
     myContext.addBean(sciStarter, true);

     ContextHandlerCollection contexts = new ContextHandlerCollection();
     contexts.setHandlers(new Handler[] { myContext });

     server.setHandler(contexts);
     return server;
}

 public static void main(String args[]) {
        String jetty_home = "/home/jetty/webServer";
        MyServer myServer = new MyServer(jetty_home);
        try {
            server = myServer.init();
            server.start();
        } catch (Exception excp) {
        }
 }
}

至于 libsetuid-linux.so 我已经使用 jetty-setuid 项目中的 mvn clean install 创建了它的本地版本。

如果 httpsPort = 2400 那么这是日志文件的详细信息:

日志

2016-02-11 15:36:16.413:INFO::main: Logging initialized @2424ms  
2016-02-11 15:36:16.593:INFO:oejs.SetUIDListener:main: Setting umask=02
2016-02-11 15:36:16.603:INFO:oejs.SetUIDListener:main: Opened         ServerConnector@b96fde{SSL,[ssl, http/1.1]}{0.0.0.0:2400}  
2016-02-11 15:36:16.603:INFO:oejs.SetUIDListener:main: Setting GID=504  
2016-02-11 15:36:16.676:INFO:oejs.SetUIDListener:main: Setting UID=504  
2016-02-11 15:36:16.680:INFO:oejs.Server:main: jetty-9.3.7.v20160115 

而当 httpsPort = 443 时,日志文件是这样的:

日志

2016-02-11 15:37:35.049:INFO::main: Logging initialized @2199ms  
2016-02-11 15:37:35.228:INFO:oejs.SetUIDListener:main: Setting umask=02

此后日志上没有任何反应,网络应用程序也无法正常工作。

这最终是一个 OS 权限问题,您需要一种解决方法。

This means any solution you come up with will also be OS specific

一个例子是使用 jetty-setuid-java artifact, and appropriate jetty-setuid-native 库来完成这个。

Make sure you fully understand how setuid functions on your desired OS before starting this effort

至于启用码头 setuid 特定部分,您可以使用 XmlConfiguration 将适当的生命周期侦听器注入您的 Server,或者您可以完全在代码中完成。

请参阅 Jetty Distribution 的 etc/jetty-setuid.xml 寻求帮助。

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"
           "http://www.eclipse.org/jetty/configure_9_3.dtd">

<!-- ================================================================ -->
<!-- Configure the Jetty SetUIDListener                                -->
<!-- ================================================================ -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <Call name="addLifeCycleListener">
    <Arg>
      <New class="org.eclipse.jetty.setuid.SetUIDListener">
        <Set name="startServerAsPrivileged">false</Set>
        <Set name="umaskOctal">002</Set>
        <Set name="username">jetty</Set>
        <Set name="groupname">jetty</Set>
        <!-- uncomment to change the limits on number of open file descriptors for root -->
        <!--
        <Call name="setRLimitNoFiles">
          <Arg>
            <New class="org.eclipse.jetty.setuid.RLimit">
              <Set name="soft">20000</Set>
              <Set name="hard">40000</Set>
            </New>
          </Arg>
        </Call>
        -->
      </New>
    </Arg>
  </Call>
</Configure>

好吧,我终于能够通过为 32 位创建一个 libsetuid-linux.so 来实现我在上面的问题中提出的问题,jetty-9.3.7 提供的是 64-位.

我如何创建 32 位 libsetuid-linux.so?

这 link 帮助了 http://www.eclipse.org/jetty/documentation/current/setting-port80-access.html 准确地说是第 5 点

但不幸的是,jetty-setuid 项目的 link 不再工作了。

此外,sudo 必须用 sudo -E 完成。