Commit 82430bf0 authored by Jonathan  Minz's avatar Jonathan Minz
Browse files

Upload New File

parent 6ba878f9
Loading
Loading
Loading
Loading
+508 −0
Original line number Diff line number Diff line
# LAFI External SFTP User Provisioning

## Purpose

This document describes the operational process used to provision and maintain external LAFI users across the THREDDS VM and the LAFO data server.

It is intended as the high-level administrative reference for understanding:

- how external users are represented on both systems;
- how the CSV registry drives provisioning;
- which scripts are used;
- how registry copies move between THREDDS and LAFO;
- how dry-run, verification, logging, and `date_added` work;
- how the complete provisioning loop is carried out reproducibly.

For code-level documentation and troubleshooting, see:

`LAFI_external_SFTP_user_provisioning_technical.md`

---

## 1. Architecture

External LAFI collaborators connect to the THREDDS VM using SFTP.

```text
External workstation
        |
        | SFTP / SSH public key
        v
THREDDS VM
144.41.15.201
        |
        | chroot: /srv/sftp
        | internal-sftp
        v
/srv/sftp/lafi
        |
        | NFSv4
        v
LAFO data server
144.41.15.88
/lafi/srv_data
```

Inside the SFTP chroot, users see:

```text
/lafi
```

which corresponds to:

```text
THREDDS host path: /srv/sftp/lafi
LAFO host path:    /lafi/srv_data
```

---

## 2. External user model

Each external user has a corresponding Unix identity on both systems.

### THREDDS VM

The THREDDS account provides:

- SSH public-key authentication;
- membership in `lafi_sftp`;
- home directory `/home/<username>`;
- `~/.ssh/authorized_keys`;
- shell `/usr/sbin/nologin`;
- forced `internal-sftp`;
- no interactive terminal access.

### LAFO data server

The LAFO account exists primarily to provide a matching NFS identity.

It has:

- the same UID as on THREDDS;
- the same primary GID as on THREDDS;
- no created home directory;
- home field `/nonexistent`;
- shell `/usr/sbin/nologin`;
- a dedicated data directory:
  `/lafi/srv_data/<username>`.

### UID/GID policy

NFS permissions depend on numeric UID/GID values. External accounts therefore use matching values on both systems.

Reserved range:

```text
20000-20999
```

Test allocation used during validation:

```text
20000  reserved
20001  lafi_testuser01
20002  lafi_testuser02
20003  lafi_testuser03
20004  lafi_testuser04
20010+ intended for production external users
```

---

## 3. User registry

Provisioning is driven by a CSV registry with the exact header:

```csv
full_name,username,uid,gid,ssh_public_key,date_added
```

Example:

```csv
Example Researcher,lafi_example,20010,20010,"ssh-ed25519 AAAA... user@computer",
```

The columns record:

- `full_name`: real-world identity;
- `username`: prescribed Unix/SFTP account name;
- `uid`: numeric UID;
- `gid`: numeric primary GID;
- `ssh_public_key`: the user's public SSH key;
- `date_added`: completion date, written automatically by the LAFO script.

Only public keys belong in the registry. Private keys must never be stored or transferred.

---

## 4. Registry copies

Two copies are used during provisioning.

### THREDDS VM

```text
external_users_working.csv
```

This is the working registry used when adding new users and running the VM-side provisioning.

### LAFO server

```text
external_users.csv
```

This is the final registry for the completed provisioning state.

The two registry copies must be synchronized at the beginning and end of every provisioning cycle.

---

## 5. Production provisioning loop

The agreed workflow is:

```text
1. Confirm LAFO and THREDDS registries are synchronized
                       |
                       v
2. Copy LAFO external_users.csv to THREDDS
                       |
                       v
3. Add new user row(s) to external_users_working.csv on THREDDS
                       |
                       v
4. Run THREDDS dry-run
                       |
                       v
5. Run THREDDS real provisioning
                       |
                       v
6. Copy external_users_working.csv to LAFO as external_users.csv
                       |
                       v
7. Run LAFO dry-run
                       |
                       v
8. Run LAFO real provisioning
                       |
                       v
9. LAFO fills date_added for newly completed users
                       |
                       v
10. Copy completed LAFO registry back to THREDDS
                       |
                       v
11. Confirm both registries are synchronized again
```

A new provisioning cycle should not begin while the two registry copies differ.

---

## 6. THREDDS provisioning script

Script:

```text
sync_lafi_external_users_vm.sh
```

Location:

```text
/home/admin120/lafi/rdm/scripts/sftp_user_creation/
```

Dry-run:

```bash
sudo ./sync_lafi_external_users_vm.sh --dry-run external_users_working.csv
```

Real run:

```bash
sudo ./sync_lafi_external_users_vm.sh external_users_working.csv
```

### Existing users

For an existing account, the script verifies:

- username;
- UID;
- GID;
- membership in `lafi_sftp`;
- home directory;
- `/usr/sbin/nologin`;
- presence of `authorized_keys`;
- presence of the registry public key in `authorized_keys`.

Existing users are not silently modified. A mismatch stops the script.

### New users

For a new row, the script:

1. checks that the username is unused;
2. checks that UID and GID are unused;
3. creates the private group;
4. creates the user;
5. adds the account to `lafi_sftp`;
6. creates `/home/<username>`;
7. creates `.ssh`;
8. installs the registered public key;
9. applies ownership and permissions;
10. checks the effective SSH policy with `sshd -T`.

---

## 7. LAFO provisioning script

Script:

```text
sync_lafi_external_users_lafo.sh
```

Location:

```text
/home/jminz/server_access/user_creation/
```

Dry-run:

```bash
sudo ./sync_lafi_external_users_lafo.sh --dry-run external_users.csv
```

Real run:

```bash
sudo ./sync_lafi_external_users_lafo.sh external_users.csv
```

### Existing users

For existing LAFO identities the script verifies:

- UID;
- GID;
- `/nonexistent` home field;
- `/usr/sbin/nologin`;
- expected data directory;
- expected directory ownership;
- mode `2755`.

### New users

For a new row, the script:

1. checks that username, UID and GID are free;
2. creates the private group;
3. creates the matching LAFO identity;
4. creates `/lafi/srv_data/<username>`;
5. sets owner `<username>:<username>`;
6. sets mode `2755`;
7. records the successful user for final date update.

After the complete LAFO pass succeeds, blank `date_added` fields are filled with the current date.

---

## 8. Dry-run mode

Both scripts support:

```text
--dry-run
```

Dry-run performs read-only validation and reports whether each row would be:

```text
VERIFY
```

or:

```text
CREATE
```

Dry-run does not:

- create users;
- create groups;
- create directories;
- install public keys;
- update the registry;
- update `date_added`;
- write provisioning logs.

Dry-run should be used before every real provisioning run.

---

## 9. Provisioning logs

Real runs write to:

```text
/var/log/lafi-user-provisioning.log
```

The log file is created as:

```text
root:root
600
```

Typical THREDDS entry:

```text
2026-08-14T20:30:00+02:00 host=thredds target=THREDDS action=CREATE user=lafi_example uid=20010 gid=20010 detail="VM SFTP account and authorized_keys created"
```

Typical LAFO actions include:

```text
VERIFY
CREATE_IDENTITY
CREATE_DIRECTORY
```

The registry records the intended/completed user state; the log records what the scripts actually did and when.

---

## 10. External user directory permissions

Each external user receives:

```text
/lafi/srv_data/<username>
```

with mode:

```text
2755
```

Typical representation:

```text
drwxr-sr-x username username
```

The tested behavior is:

| Operation | Own directory | Another external user's directory |
|---|---:|---:|
| List | yes | yes |
| Read/download | yes | yes |
| Upload | yes | no |
| Modify | yes | no |
| Delete file | yes | no |
| Rename file | yes | no |
| Rename another user's top-level directory | no | no |

The top-level directory name is treated as canonical:

```text
/lafi/srv_data/<username>
```

An administrator can technically rename it with `sudo`, but doing so would conflict with the provisioning logic because the script expects the canonical directory to exist.

---

## 11. SSH policy

External users belong to:

```text
lafi_sftp
```

The SSH drop-in is:

```text
/etc/ssh/sshd_config.d/60-lafi-sftp.conf
```

with:

```text
Match Group lafi_sftp
    ChrootDirectory /srv/sftp
    ForceCommand internal-sftp -d /lafi -u 022
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    PubkeyAuthentication yes
    AllowTcpForwarding no
    X11Forwarding no
    AllowAgentForwarding no
    PermitTunnel no
```

This provides:

- key-only authentication;
- SFTP-only access;
- no interactive terminal;
- no forwarding or tunnelling;
- automatic start in `/lafi`;
- uploaded file umask `022`.

---

## 12. Validation completed

The complete process was tested with:

```text
lafi_testuser01
lafi_testuser02
lafi_testuser03
lafi_testuser04
```

The following were validated:

- existing-user verification;
- new-user creation;
- public-key installation;
- effective SSH/SFTP restrictions;
- matching UID/GID creation on both hosts;
- LAFO data-directory creation;
- correct `2755` ownership/permissions;
- `date_added` update;
- dry-run behavior;
- provisioning logs;
- repeated idempotent runs;
- cross-user read/write restrictions;
- complete end-to-end SFTP access from an external workstation.

---

## 13. Operational rules

1. Start every provisioning cycle with synchronized registries.
2. Add new users to the THREDDS working registry.
3. Run dry-run before every real provisioning run.
4. Never reuse a UID/GID without checking both systems.
5. Never store private SSH keys.
6. Only public keys belong in the registry.
7. Treat `/lafi/srv_data/<username>` as the canonical managed directory.
8. Do not silently modify unexpected existing account state.
9. Copy the LAFO-completed registry back to THREDDS after provisioning.
10. Confirm registry synchronization again before the next cycle.