2

By default, rundeck runs on ports 4440 / 4443. Now, for security reasons which really ought to be obvious, I don't want this sensitive service binding to high-numbered ports. Once HTTPS is set up, that should ameliorate some of my concerns, but still...

Is there a way to have the rundeckd service bind to port 80 as root and then drop down to the rundeck user?

To be abundantly clear, I am not asking "How can I get rundeck to run on a different port?", because I can modify /etc/rundeck/profile and get it to run on any non-registered port just fine.

4
  • 1
    I'm not sure I follow "for security reasons which really ought to be obvious, I don't want this sensitive service binding to high-numbered ports"...wouldn't you benefit from a higher port number against automated scanners targeting well-known ports such as 80 and 443? Jan 18, 2016 at 21:53
  • This would also not require root privs. Jan 18, 2016 at 21:58
  • It requires root to bind to a registered ports. This is behind a firewall, so port scans aren't an issue, but if somehow an unprivileged user is compromised, I don't want them to be able to bind to a port that another host would expect to only be handled by a privileged process / user. Jan 19, 2016 at 2:52
  • @ParthianShot, you will need to code rundeck for that I guess. See my updated answer.
    – Diamond
    Jan 20, 2016 at 11:32

3 Answers 3

2

Is there a way to have the rundeckd service bind to port 80 as root and then drop down to the rundeck user?

There is no out of the box solution for this as far as I can see. For what you want to achieve, the program should use Privilege separation:

In computer programming and computer security, privilege separation is a technique in which a program is divided into parts which are limited to the specific privileges they require in order to perform a specific task. This is used to mitigate the potential damage of a computer security attack.

A common method to implement privilege separation is to have a computer program fork into two processes. The main program drops privileges, and the smaller program keeps privileges in order to perform a certain task. The two halves then communicate via a socket pair. Thus, any successful attack against the larger program will gain minimal access, even though the pair of programs will be capable of performing privileged operations.

Privilege separation is traditionally accomplished by distinguishing a real user ID/group ID from the effective user ID/group ID, using the setuid(2)/setgid(2) and related system calls, which were specified by POSIX. If these are incorrectly positioned, gaps can allow widespread network penetration.

As mentioned above, a process can call the setuid(2), and setgid(2) to drop the privilages after starting as root.

And for all this to work you will possibly need to do fundamental changes in rundeck code, if you are that desperate.

Please see : How and why Linux daemons drop privileges and

https://unix.stackexchange.com/questions/21282/drop-process-privileges

Or you can use authbind that allows non-root programs to bind() to lower ports.


And for binding to another port according to Rundeck Installtion guide: System properties, among others:

You can customize the launcher behavior by using some java system properties.

Specify these properties using the normal -Dproperty=value commandline options to the java command:

server.http.port The HTTP port to use for the server, default "4440"
server.https.port The HTTPS port to use or the server, default "4443"
1
  • "And for binding to another port according to Rundeck Installtion guide: System properties" does not work in Rundeck 3.2.3-20200221 (2020-02-21) (from rpm package installation)
    – lobi
    Mar 11, 2020 at 16:54
0

One classic approach is to set up an nginx proxy. On ubuntu and probably most flavors of linux, install the nginx package and put a configuration file in /etc/nginx/sites-available. My file looks like this:

server {
    listen 443;
    ssl_certificate           /etc/nginx/my_domain.crt;
    ssl_certificate_key       /etc/nginx/my_domain.key;

    # Limit to VPN
    allow 10.1.0.0/16;
    deny all;

    ssl on;
    ssl_session_cache  builtin:1000  shared:SSL:10m;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "frame-ancestors 'self'";
    location / {
        proxy_pass http://localhost:4440;
        proxy_read_timeout 90;
    }
}

Then create a symlink in /etc/nginx/sites-enabled/ to /etc/nginx/sites-available/rundeck and restart. Obviously, similar packages like haproxy could do the same for you.

1
  • 1
    The specific scenario I'm concerned about is rundeck crashing and another (non-root) process binding to rundeck's port. It looks like the only real workable solutions for that are either SELinux or updating rundeck code itself. Setting up a reverse proxy would solve certain problems, but that specific problem would remain unsolved. Dec 18, 2019 at 22:01
0

If you're using AWS, an alternative approach is to use an Application Load Balancer (ALB) to proxy for you. This way, any updates/configuration changes you make on the instance won't affect this; you are decoupling the proxy from the server OS. This should alleviate OP's concern in his comment under Karl DeBisschop's comment.

Full documentation here, but an overview is:

Configure an ALB. This will include configuring your "Listeners", which will "listen" for traffic destined to port 80 from the outside (e.g. clients), and it will include configuring your "Target Group", which will "listen" for traffic destined to port 4440 from your ALB's Listener.

The flow is: Client requests 80 -> Listener receives request -> Listener "forwards" request to Target Group -> EC2 instance receives on port 4440

--

Note that you must register EC2 to the Target Group manually, or via the auto scaling group configuration. If after registering you get a 302 health check error, this just means your health check path is wrong. A quick way to correct this is to do...

curl -vL <your URL destination>

The output should show both your 302 redirect as well as the 200 response (assuming your web app is running OK). Find the path under the 302 redirect response, and paste that into the Target Group health check path:

  > GET / HTTP/1.1
  > Host: XXXXXXXXX.us-east-1.elb.amazonaws.com
  > User-Agent: curl/7.64.1
  > Accept: */*
  >
  < HTTP/1.1 302 Found
  < Date: Wed, 11 Mar 2020 18:19:57 GMT
  < Content-Length: 0
  < Connection: keep-alive
  < Set-Cookie: JSESSIONID=XXXXXXXXXX; Path=/; HttpOnly
  < Expires: Thu, 01 Jan 1970 00:00:00 GMT
  < Location: http://XXXXXXXXX.us-east-1.elb.amazonaws.com/user/login
  <
  * Connection #0 to host XXXXXXXXX.us-east-1.elb.amazonaws.com left intact
  * Issue another request to this URL: 'http://XXXXXXXXX.us-east-1.elb.amazonaws.com/user/login'
  [omitted]
  * Connected to XXXXXXXXX.us-east-1.elb.amazonaws.com (10.1.1.1) port 80 (#0)
  > GET /user/login HTTP/1.1    <-------------- this path is what you want

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .