Apply Config Server

Deploy to servers/run scripts on servers via http

License

License

Categories

Categories

Net
GroupId

GroupId

biz.netcentric.ops.applyserver
ArtifactId

ArtifactId

apply-server
Last Version

Last Version

1.6.3
Release Date

Release Date

Type

Type

jar
Description

Description

Apply Config Server
Deploy to servers/run scripts on servers via http
Project URL

Project URL

https://github.com/Netcentric/apply-server
Source Code Management

Source Code Management

https://github.com/Netcentric/apply-server.git

Download apply-server

How to add to project

<!-- https://jarcasting.com/artifacts/biz.netcentric.ops.applyserver/apply-server/ -->
<dependency>
    <groupId>biz.netcentric.ops.applyserver</groupId>
    <artifactId>apply-server</artifactId>
    <version>1.6.3</version>
</dependency>
// https://jarcasting.com/artifacts/biz.netcentric.ops.applyserver/apply-server/
implementation 'biz.netcentric.ops.applyserver:apply-server:1.6.3'
// https://jarcasting.com/artifacts/biz.netcentric.ops.applyserver/apply-server/
implementation ("biz.netcentric.ops.applyserver:apply-server:1.6.3")
'biz.netcentric.ops.applyserver:apply-server:jar:1.6.3'
<dependency org="biz.netcentric.ops.applyserver" name="apply-server" rev="1.6.3">
  <artifact name="apply-server" type="jar" />
</dependency>
@Grapes(
@Grab(group='biz.netcentric.ops.applyserver', module='apply-server', version='1.6.3')
)
libraryDependencies += "biz.netcentric.ops.applyserver" % "apply-server" % "1.6.3"
[biz.netcentric.ops.applyserver/apply-server "1.6.3"]

Dependencies

compile (6)

Group / Artifact Type Version
org.apache.commons : commons-exec jar 1.3
org.apache.commons : commons-compress jar 1.19
commons-cli : commons-cli jar 1.4
commons-io : commons-io jar 2.6
org.apache.commons : commons-lang3 jar 3.7
commons-net : commons-net jar 3.6

test (2)

Group / Artifact Type Version
junit : junit jar 4.12
org.mockito : mockito-core jar 2.13.0

Project Modules

There are no modules declared in this project.

Maven Central License Build Status SonarCloud Status SonarCloud Coverage

Apply Server

Overview

The idea behind "Apply Server" is to control a remote computer via http (instead of ssh). Use cases are

  • Deploy configuration files and restart services (e.g. httpd)
  • Run maintenance scripts

Apply Server was inspired by the config via http approach of nginx unit [1] and allows to turn apache, solr, or any other server into a http-configurable by just starting another process that acts as agent on target system. Using the Apply Server, it is easy to deploy well-versioned configuration artifacts to target systems via a CI/CD pipeline (like e.g. Jenkins). Obviously CI/CD tools also support ssh, but ssh access can be harder to obtain than opening an http port, also ssh often does not make it easy to transfer/unpack configuration tars (often a change of user is required, permissions can be a challenge). In contrary, an Apply Server can just be started with the same user as the service is running (hence all correct permissions are automatically in placed) and the start parameters ensure its actions are limited to what the Apply Server was started with.

Why not use Puppet/Chef/Ansible?

In general all these tools are very useful (e.g. even to install the apply server agent, see Setup Apply Server via puppet below), however their strength is more in setting up a base configuration (OS level, files, directories, permissions, services etc.). For deployments of constantly changing code/configuration they lack support for a workflow where any people (including non-technical people) can make code/configuration travel across environments - here CI/CD servers like Jenkins have their strength. Many tools allow for deployment via http (e.g. Tomcat, Adobe AEM, etc.), Apply Server makes it easy to deploy Apache/Varnish/etc. configuration in the same way as software.

Why another http server?

There are many options for starting an http server already [2], but none that focus on

  • Running scripts on target system (and make the result history visible)
  • Upload/download files to/from a target directory in zip/tgz format (zipping/unzipping on the fly)
  • Have minimal dependencies (Apply Server is a lightweight fat jar and has only a JRE as dependency)

[1] https://www.nginx.com/blog/nginx-unit-1-0-released/ https://www.infoq.com/news/2018/05/nginx-unit-dynamic-web-server

[2] https://gist.github.com/willurd/5720255

Security

There are two options to lock down access directly in apply server:

  • IP restriction: Only POST requests from given IP/IP range are accepted
  • API Key Secret: Only requests that carry the correct API key as header are accepted

Generally security restrictions are given as start parameters of the server. Additionally if needed, also the firewall of the production systems can be configured to allow only requests from certain IPs.

Size and dependencies

Apply server is very lightweight: It is just ~130 KB of size while all dependencies are included in the jar directly (e.g. commons-cli) and only the actual required classes are kept in the file (proguard-maven-plugin is used for shrinking).

Apply server is a simple jar that can be run via java -jar on JREs > 1.8 and has no other dependencies.

Usage

Start Server on target system

$ java -jar apply-server-1.6.2.jar
usage: apply-server
 -c,--command <arg>                   allows to map URL paths to certain
                                      scripts: -c myscript=myscript.sh
                                      will run myscript.sh in destination
                                      upon POST requests to /myscript.
                                      Multiple -c options can be provided,
                                      the script provided with -s is the
                                      default if no command matches
 -d,--destination <arg>               the target destination dir. This is
                                      where payload is extracted to and
                                      relative script paths are evaluated
                                      against.
 -dl,--enable-download                enables download of the current
                                      files at destination via URL
                                      /download.tar.gz
 -du,--disable-upload                 disable upload (only allow script
                                      execution and potentially download
                                      if -ed is given)
 -ed,--exclude-from-download <arg>    Regex for files to be excluded from
                                      download
 -ef,--exclude-from-filtering <arg>   to supply a regex of files to be
                                      explicitly excluded from filtering
 -ip,--ip-range <arg>                 when supplied, only upload/script
                                      execution requests from the given
                                      IP/IP range are accepted (can be
                                      regular IP like '20.30.40.50' or
                                      CIDR range like '20.30.40.50/24').
                                      Useful to restrict clients that can
                                      make changes to the system. Has no
                                      effect for GET requests.
 -k,--api-key <arg>                   when supplied, the given api key has
                                      to be sent with every request as
                                      header 'apikey'
 -nf,--no-filtering                   by default the incoming files are
                                      filtered using the OS env and
                                      '_apply.sh' - using this option will
                                      disable that.
 -o,--optional-payload                allows to not send a payload to be
                                      filtered/extracted with the request
                                      but just to run the scripped as
                                      mapped
 -p,--port <arg>                      the port the server is listening to
 -P,--properties <arg>                the properties file name to apply
                                      the config  - if not given only env
                                      variables will be taken into account
 -pid,--pid-file <arg>                will write the the pid file
 -s,--script <arg>                    the script name to run (relative to
                                      destination dir after extracting) -
                                      defaults to "_apply.sh". If a
                                      command is matched the command takes
                                      precedence. The script can already
                                      exist in the destination or be part
                                      of the uploaded package.

Examples

Starting Apply Server Agent

start server to take and unpack zip, filter it and run an apply script afterwards:

java -jar apply-server-1.6.2.jar -p 448 -d /path/to/destination -s applyConfig.sh --api-key MT7HpOKnx5 # secured via api key secret
java -jar apply-server-1.6.2.jar -p 448 -d /path/to/destination -s applyConfig.sh --ip-range 100.200.300.0/16 # secured via ip range protection

start a server to run scripts remotely

java -jar apply-server-1.6.2.jar -p 448 -d /path/to/scripts --optional-payload -c /script1=myScript1.sh -c /script2=myScript2.sh --api-key MT7HpOKnx5  # secured via api key secret
# alternatively (or in combination) use --ip-range 100.200.300.0/16 to secure via ip range protection

Client Usage

Push configurations to it from elsewhere and run a script to use the new files.

The full API documentation is at https://netcentric.github.io/apply-server/index.html based on the OpenAPI v3 yaml file at docs/openapi.yaml.

Upload and run default script

To push configurations to the target system and run the script as provided by server start:

curl -X POST -H "apikey: MT7HpOKnx5" --data-binary "@path/to/my-config-package.tar.gz" http://myserver:448/package-name.tar.gz

alternatively the format parameter can be given to not include the filename in path:

curl -X POST -H "apikey: MT7HpOKnx5" --data-binary "@path/to/my-config-package.tar.gz" http://myserver:448?format=tar.gz

Run scripts only

No upload required, often used along with multiple -c parameters

if the server was started with

java -jar apply-server-1.6.2.jar -d /path/to/scripts --optional-payload -c /script1=myScript1.sh -c /script2=myScript2.sh ...

the following two commands will run myScript1.sh or myScript2.sh respectively:

curl -X POST -H "apikey: MT7HpOKnx5" http://myserver:448/script1
curl -X POST -H "apikey: MT7HpOKnx5" http://myserver:448/script2

Using Apply Server with Apache Httpd

A simple script restartApache.sh looks as follows:

# fail immedately in case of invalid config
set -e  
# test the config
apachectl configtest
# restart gracefully without downtime (will start server if not yet running)
apachectl graceful

The following start command can be used to start the apply server:

java -jar apply-server-1.6.2.jar -d /etc/apache2 -p 8449 -s restartApache.sh -nf 

To perform a restart, the curl command

$ curl -X POST http://localhost:8449/?format=zip --data-binary @httpd-config.zip 

produces the following output for the case the configuration is correct:

Request from /0:0:0:0:0:0:0:1:55878 at 2020-02-20 21:29:54
Processing entity /
--- Placing files:
Filtering is disabled
Extracted httpd.conf                                         (not filtered)
--- Executing apply script: restartSystemApache.sh
Syntax OK
Restarted apache.
--- Apply script 'restartSystemApache.sh' returned 0
Finished after 166ms

If the config contains an error it looks as follows:

Request from /0:0:0:0:0:0:0:1:56008 at 2020-02-20 21:33:17
Processing entity /
--- Placing files:
Filtering is disabled
Extracted httpd.conf                                         (not filtered)
--- Executing apply script: restartSystemApache.sh
AH00526: Syntax error on line 1 of /usr/local/etc/httpd/httpd.conf:
Invalid command 'InvalidDirective', perhaps misspelled or defined by a module not included in the server configuration
--- Apply script 'restartSystemApache.sh' returned 1
Finished after 34ms

For the error case the http response code is 500.

Listing past executions (via GET)

Just calling http://myserver:448 in browser will list all past executions and give links to see the logs of each execution.

Setup Apply Server via puppet

The following snippet will download and start the apply server with the given arguments:

### Parameters ###
$apply_server_version = '1.6.2'
$apply_server_target_dir = '/opt/files'
$apply_server_port = 8089
$apply_server_additional_arguments = ' -du -dl'
$apply_server_dir = '/opt/apply-server'
$nexus_base_url = 'https://repo.int.netcentric.biz/nexus/service/local/repositories'
 
### Provision the jar ###
$apply_server_path = "${apply_server_dir}/apply-server-${apply_server_version}.jar"
notice("Ensuring apply server exists: ${apply_server_version}")
file { $apply_server_dir:
  ensure  => directory,
}
 
$curl_command = "/usr/bin/curl -u ${netcentric::maven::nexus_user}:${netcentric::maven::nexus_password} -o ${apply_server_path} ${nexus_base_url}/netcentric-releases/content/biz/netcentric/ops/applyserver/apply-server/${apply_server_version}/apply-server-${apply_server_version}.jar"
exec { 'retrieve_apply_server_jar':
  command => "/usr/bin/echo Downloading ${apply_server_path} && ${curl_command}",
  creates => $apply_server_path,
  require => [ File[$apply_server_dir] ],
}
 
file { $apply_server_path:
  ensure  => file,
  require => [ Exec["retrieve_apply_server_jar"] ],
}
 
### Run the service ###
# using the base command with port allows to run multiple servers
$base_command = "java -jar ${apply_server_path} -p ${apply_server_port}"
service { "apply-server-${apply_server_port}":
  ensure  => running,
  start   => "/usr/bin/nohup ${base_command} -d ${apply_server_target_dir} ${apply_server_additional_arguments} < /dev/null > ${apply_server_dir}/apply-server.log 2>&1 &",
  pattern => $base_command,
  require => [ File[$apply_server_path] ],
}  
biz.netcentric.ops.applyserver

Netcentric

A Cognizant Digital Business

Versions

Version
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0