Skip to content

Commit

Permalink
Merge branch 'develop' into chore/update-server
Browse files Browse the repository at this point in the history
  • Loading branch information
krusche authored May 25, 2024
2 parents a35e925 + 21ead1e commit c9a8390
Show file tree
Hide file tree
Showing 108 changed files with 1,787 additions and 443 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ dependencies {
// Note: jgit.htt.server is not compatible with jakarta yet and neither is there a timeline. Hence, we had to add the sourcefiles to our repository. Once the compatibility is given, we can switch back to the maven dependency.
// implementation "org.eclipse.jgit:org.eclipse.jgit.http.server:${jgit_version}"

implementation "org.apache.sshd:sshd-core:2.12.1"
implementation "org.apache.sshd:sshd-git:2.12.1"
implementation "org.apache.sshd:sshd-osgi:2.12.1"
implementation "org.apache.sshd:sshd-sftp:2.12.1"

implementation "oauth.signpost:signpost-core:2.1.1"
implementation "oauth.signpost:signpost-commonshttp4:2.1.1"

Expand Down
1 change: 1 addition & 0 deletions docker/artemis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ services:
expose:
- "8080"
- "5701" # Hazelcast
- "7921" # Git SSH
networks:
- artemis

Expand Down
3 changes: 3 additions & 0 deletions docker/nginx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ services:
- ./nginx/timeouts.conf:/etc/nginx/conf.d/timeouts.conf:ro
- ./nginx/artemis-nginx.conf:/etc/nginx/conf.d/artemis-nginx.conf:ro
- ./nginx/artemis-upstream-single-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro
- ./nginx/artemis-ssh-upstream-single-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro
- ./nginx/artemis-server.conf:/etc/nginx/includes/artemis-server.conf:ro
- ./nginx/dhparam.pem:/etc/nginx/dhparam.pem:ro
- ./nginx/nginx_503.html:/usr/share/nginx/html/503.html:ro
Expand All @@ -29,10 +30,12 @@ services:
ports:
- "80:80"
- "443:443"
- "7921:7921" # Git SSH
# expose the port to make it reachable docker internally even if the external port mapping changes
expose:
- "80"
- "443"
- "7921" # Git SSH
healthcheck:
test: service nginx status || exit 1
start_period: 60s
Expand Down
3 changes: 3 additions & 0 deletions docker/nginx/artemis-ssh-upstream-multi-node.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
server artemis-app-node-1:7921;
server artemis-app-node-2:7921;
server artemis-app-node-3:7921;
1 change: 1 addition & 0 deletions docker/nginx/artemis-ssh-upstream-single-node.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
server artemis-app:7921;
10 changes: 10 additions & 0 deletions docker/nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ http {

include /etc/nginx/conf.d/*.conf;
}

stream {
upstream artemis {
include includes/artemis-ssh-upstream.conf;
}
server {
listen 7921;
proxy_pass artemis;
}
}
1 change: 1 addition & 0 deletions docker/test-server-multi-node-mysql-localci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ services:
restart: always
volumes:
- ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro
- ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro
- type: bind
source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem}
target: "/certs/fullchain.pem"
Expand Down
1 change: 1 addition & 0 deletions docker/test-server-multi-node-postgresql-localci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ services:
restart: always
volumes:
- ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro
- ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro
- type: bind
source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem}
target: "/certs/fullchain.pem"
Expand Down
107 changes: 107 additions & 0 deletions docs/admin/setup/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,110 @@ variables (`Spring relaxed binding <https://github.com/spring-projects/spring-bo
Ensure restrictive access to the configuration files, so that access is only
possible for the system account that runs Artemis and administrators.


SSH Access
----------

Follow the following steps to create and manage SSH key pairs,
distribute them across multiple nodes via Ansible, configure the
system to use these keys, and adapt Nginx to enable SSH routing.

Creating Key Pairs for Each Supported Algorithm
"""""""""""""""""""""""""""""""""""""""""""""""

Generate key pairs for the supported algorithms (RSA, ECDSA, and Ed25519)
using the ssh-keygen command. Here's how you can do it:

.. code-block:: bash
# Generate RSA key pair
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa
# Generate Ed25519 key pair
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
Adding Key Pairs to VM via Ansible
""""""""""""""""""""""""""""""""""

Use Ansible to distribute the generated key pairs to all VMs in your
infrastructure. Here's an example Ansible playbook to achieve this:

.. code-block:: yaml
- name: Distribute SSH keys to VMs
hosts: all
vars:
key_dir: "/path/to/keys"
tasks:
- name: Copy RSA key pair to VM
copy:
src: "{{ key_dir }}/id_rsa"
dest: "~/.ssh/id_rsa"
mode: '0600'
- name: Copy RSA public key to VM
copy:
src: "{{ key_dir }}/id_rsa.pub"
dest: "~/.ssh/id_rsa.pub"
mode: '0644'
Configuring System to Use Keys
""""""""""""""""""""""""""""""

Ensure the configuration variables point to the folder containing the keys. You can set this in your
Ansible playbook or configuration management tool.

In a multinode setup, it is crucial that all nodes use the same set of keys to ensure hosts can communicate with all
nodes correctly. Ensure the key distribution playbook is applied to all nodes in the cluster.

For Artemis to find the key set `artemis.version-control.ssh-host-key-path` to the path where you stored the keys.

Adapting Nginx to Enable SSH Routing
""""""""""""""""""""""""""""""""""""

To enable SSH routing through Nginx, you can set up an SSH proxy. However, Nginx by itself does
not support SSH, but you can use Nginx to reverse proxy an SSH service (e.g., using sslh to multiplex SSH and HTTPS).

Configure sslh to listen on port 443 (to handle both HTTPS and SSH), by editing the sslh configuration
file (e.g., /etc/default/sslh):

.. code-block:: text
RUN=yes
DAEMON=/usr/sbin/sslh
DAEMON_OPTS="--user sslh --listen 0.0.0.0:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:8443"
Configure Nginx to proxy HTTPS traffic, by adapting the configuration file to listen on port 8443 for HTTPS:

.. code-block:: nginx
server {
listen 8443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Restart sslh and Nginx:

.. code-block:: bash
sudo systemctl restart sslh
sudo systemctl restart nginx
By following these steps, you ensure that your key pairs are properly generated and distributed across all
nodes, the configuration is set up to point to the folder with the keys, and Nginx is adapted to handle
SSH routing through a proxy setup.
2 changes: 1 addition & 1 deletion src/main/java/de/tum/in/www1/artemis/config/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public final class Constants {

public static final int PASSWORD_MIN_LENGTH = 8;

public static final int PASSWORD_MAX_LENGTH = 50;
public static final int PASSWORD_MAX_LENGTH = 100;

public static int COMPLAINT_LOCK_DURATION_IN_MINUTES = 24 * 60; // 24h; Same as in artemisApp.locks.acquired

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.tum.in.www1.artemis.config.localvcci;

import static de.tum.in.www1.artemis.config.Constants.PROFILE_LOCALVC;

import org.eclipse.jgit.http.server.GitServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -8,30 +10,30 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import de.tum.in.www1.artemis.service.connectors.localvc.LocalVCServletService;
import de.tum.in.www1.artemis.config.localvcci.service.ArtemisGitServletService;

/**
* Configuration of the JGit Servlet that handles fetch and push requests for local Version Control.
*/
@Configuration
@Profile("localvc")
@Profile(PROFILE_LOCALVC)
public class JGitServletConfiguration {

private static final Logger log = LoggerFactory.getLogger(JGitServletConfiguration.class);

private final LocalVCServletService localVCServletService;
private final ArtemisGitServletService artemisGitServlet;

public JGitServletConfiguration(LocalVCServletService localVCServletService) {
this.localVCServletService = localVCServletService;
public JGitServletConfiguration(ArtemisGitServletService artemisGitServlet) {
this.artemisGitServlet = artemisGitServlet;
}

/**
* @return GitServlet (Git server implementation by JGit) configured with a repository resolver and filters for fetch and push requests.
*/
@Bean
public ServletRegistrationBean<GitServlet> jgitServlet() {
ArtemisGitServlet gitServlet = new ArtemisGitServlet(localVCServletService);
log.debug("Registering ArtemisGitServlet for handling fetch and push requests to [Artemis URL]/git/[Project Key]/[Repository Slug].git");
return new ServletRegistrationBean<>(gitServlet, "/git/*");
log.info("Registering ArtemisGitServlet for handling fetch and push requests to [Artemis URL]/git/[Project Key]/[Repository Slug].git");
return new ServletRegistrationBean<>(artemisGitServlet, "/git/*");
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package de.tum.in.www1.artemis.config.localvcci;
package de.tum.in.www1.artemis.config.localvcci.service;

import static de.tum.in.www1.artemis.config.Constants.PROFILE_LOCALVC;

import jakarta.annotation.PostConstruct;

import org.eclipse.jgit.http.server.GitServlet;
import org.eclipse.jgit.transport.ReceivePack;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import de.tum.in.www1.artemis.domain.User;
import de.tum.in.www1.artemis.service.connectors.localvc.LocalVCFetchFilter;
import de.tum.in.www1.artemis.service.connectors.localvc.LocalVCPostPushHook;
import de.tum.in.www1.artemis.service.connectors.localvc.LocalVCPrePushHook;
Expand All @@ -13,15 +19,26 @@
/**
* This class configures the JGit Servlet, which is used to receive Git push and fetch requests for local VC.
*/
@Profile("localvc")
public class ArtemisGitServlet extends GitServlet {
@Profile(PROFILE_LOCALVC)
@Service
public class ArtemisGitServletService extends GitServlet {

private final LocalVCServletService localVCServletService;

/**
* Constructor for ArtemisGitServlet.
*
* @param localVCServletService the service for authenticating and authorizing users and retrieving the repository from disk
*/
public ArtemisGitServlet(LocalVCServletService localVCServletService) {
public ArtemisGitServletService(LocalVCServletService localVCServletService) {
this.localVCServletService = localVCServletService;
}

/**
* Initialize the ArtemisGitServlet by setting the repository resolver and adding filters for fetch and push requests.
*/
@PostConstruct
public void init() {
this.setRepositoryResolver((request, name) -> {
// request – the current request, may be used to inspect session state including cookies or user authentication.
// name – name of the repository, as parsed out of the URL (everything after /git/).
Expand All @@ -37,7 +54,7 @@ public ArtemisGitServlet(LocalVCServletService localVCServletService) {
this.setReceivePackFactory((request, repository) -> {
ReceivePack receivePack = new ReceivePack(repository);
// Add a hook that prevents illegal actions on push (delete branch, rename branch, force push).
receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, request));
receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, (User) request.getAttribute("user")));
// Add a hook that triggers the creation of a new submission after the push went through successfully.
receivePack.setPostReceiveHook(new LocalVCPostPushHook(localVCServletService));
return receivePack;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.tum.in.www1.artemis.config.localvcci.ssh;

import java.security.PublicKey;

import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.digest.BuiltinDigests;

public class HashUtils {

public static String getSha512Fingerprint(PublicKey key) {
return KeyUtils.getFingerPrint(BuiltinDigests.sha512.create(), key);
}
}
Loading

0 comments on commit c9a8390

Please sign in to comment.