# System Administration

# Linux



# Linux Network Monitoring

Here’s a list of some useful terminal based network monitoring utilties:

[https://askubuntu.com/questions/257263/how-to-display-network-traffic-in-the-terminal](https://askubuntu.com/questions/257263/how-to-display-network-traffic-in-the-terminal "https://askubuntu.com/questions/257263/how-to-display-network-traffic-in-the-terminal")

Best one we have used so far, is BMon.

It’s available as: sudo apt install bmon

Can be run with:

```bash
sudo bmon
```

…where the -p is the interface.

When running you can see this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-01/scaled-1680-/t3wdawYeVZVVjrK3-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-01/t3wdawYeVZVVjrK3-image.png)

<div class="rich-media-item mediaSingleView-content-wrap image-center cc-1770117" data-layout="center" data-node-type="mediaSingle" data-renderer-start-pos="337" data-width="760" data-width-type="pixel" id="bkmrk--1"></div>By default, you can scroll through the available interfaces, and see stats for each.

If you want to run it for a specfic NIC, use this:

```bash
sudo bmon -p nebula200
```

# Linux: Impersonating Users

<p class="callout info">NOTE: This page was created to generalize the technique of impersonating a system account that has no defined shell, and no known password. Specifically, it was documented as a means to add functionality to a Jenkins build server (where the jenkins account has a disabled shell).</p>

### Solution

If you come across a software package on a Linux host that runs with a system account (one without a known password or defined shell), here are ways to do things as a system account user.

The above check in /etc/passwd will indicate what shell is defined for the Jenkins user.

Normally, it will be set to: /bin/false.  
This means that the user's shell is disabled.

Obviously the above screenshot indicates the jenkins shell is: /bin/bash.  
But, this was set as a permanent fix, that could have been a drastic solution, and not totally necessary, since we've learned since then.

Here are a couple of things we can do, when we must install things for the jenkins user (that will execute them).

##### 1. Temporarily switch to Another User (if it has no defined shell).

If the login shell is /bin/false or /user/sbin/nologin, you won't be able to use `su jenkins` <span class="hljs-variable">directly.  
Instead, you can run either of these:  
</span>

```bash
sudo -u jenkins -s --shell /bin/bash
```

Or:

```bash
sudo -u jenkins bash
```

This gives you a shell as `jenkins`.

##### 2. Permanent Change (If You Want to Allow Logins)

If you want to enable login for the `jenkins` user, you can change its shell to `/bin/bash`:

```
sudo usermod -s /bin/bash jenkins
```

Now, you can switch users normally with:  
`sudo su jenkins`  
  
Or (if running as root):  
`su - jenkins`

##### 3. Running a Specific Command as the Jenkins User

If you only need to run a single command as `jenkins`, you can use:

```bash
sudo su jenkins <command>
```

For example:

```bash
sudo -u jenkins whoami
```

Or:

```bash
sudo -u jenkins ssh-keygen -t rsa -b 4096 -f /var/lib/jenkins/.ssh/id_rsa
```

### Examples

Here are examples of how to use the above technique to impersonate a user.

##### Installing SSH Keys

```bash
sudo -u jenkins mkdir -p /var/lib/jenkins/.ssh
sudo -u jenkins chmod 700 /var/lib/jenkins/.ssh
sudo -u jenkins ssh-keygen -t rsa -b 4096 -f /var/lib/jenkins/.ssh/id_rsa
```

##### Configuring Git for a Jenkins User

```bash
sudo -u jenkins git config --global user.name "Jenkins CI"
sudo -u jenkins git config --global user.email "jenkins@example.com"
```

##### Getting Environment Variables for a User

```bash
sudo -u jenkins env
```

# Installing Node.js on Ubuntu

This page shows how to install Node.js on an Ubuntu 22 host.

It only shows how to install a single, global Node.js version.

If you need multiple Node.js version support, see this page to work with NVM (Node Version Manager): [NVM - Node.js Version Manager](https://wiki.galaxydump.com/link/109)

<p class="callout warning">NOTE: If you will need to work with multiple versions of Node.js on the same host, such as performing automated builds of Angular apps for different Angular versions, then these steps won't work for you.  
Instead, you need to install NVM to manage those versions of Node.js.</p>

#### Removing Old Versions

Before installing Node.js, it is necessary to remove any existing version of Node from the host.


Be sure to remove any existing versions of Node.js before continuing.  
Normally, you can do this through the package manager that installed it.

<p class="callout warning">NOTE: If you're running Ubuntu 22, you will need to follow this: [Ubuntu 22: Removing Existing Node.js](https://wiki.galaxydump.com/link/115)</p>

Check if Node.js was installed via APT:

```bash
dpkg -l | grep nodejs
```

If found, remove it with this:

```bash
sudo apt remove --purge nodejs npm -y
sudo apt autoremove -y
```

If Node.js was installed, via NVM (Node Version Manager), and you want to remove a version that NVM is maintaining, do this:

```bash
nvm uninstall <version>
```

Ex; `nvm uninstall 16` or `nvm uninstall 18`

#### Install Single Node.js Version<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

<p class="callout info">NOTE: This will install a single Node.js version.  
See the later section for installing multiple Node.js versions.</p>

Use this to install the desired version of Node.js:

```bash
sudo apt update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
sudo apt-get install nodejs -y
```

Verify the node version with this: <span class="prismjs cc-zn1qqt" data-code-lang="" data-ds--code--code-block="" data-testid="renderer-code-block">`<span class="" data-ds--code--row="" data-testid="renderer-code-block-line-1"><span class="">sudo node -v</span></span>`</span>

# Ubuntu 22: Removing Existing Node.js

Ubuntu 22 comes with an existing version of node.js, v12, that is difficult to upgrade.

This is because the libnode-dev package is holding onto it, and we must release it, first.

#### Removing Conflicting Versions<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Run these to remove the conflicting package:

`sudo apt-get remove libnode-dev`

`sudo apt-get update`

Now, you can remove old versions of node.js:

```bash
sudo apt remove nodejs
cd /etc/apt/sources.list.d 
sudo rm nodesource.list
sudo apt --fix-broken install
sudo apt update
sudo apt remove nodejs
sudo apt remove nodejs-doc
```

# OpnSense Notes

Here’s a collection of notes and details for the house router setup.

### Build<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Software: OpnSense 24.7.4\_1

Hardware: Gigabyte H97N-Wifi motherboard

Processor: Intel i5-4460

### Setup<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Base setup was done following this article: [https://homenetworkguy.com/how-to/set-up-a-fully-functioning-home-network-using-opnsense/#unbound-dns-general](https://homenetworkguy.com/how-to/set-up-a-fully-functioning-home-network-using-opnsense/#unbound-dns-general "https://homenetworkguy.com/how-to/set-up-a-fully-functioning-home-network-using-opnsense/#unbound-dns-general")

Dynamic DNS Setup is here: [https://oga.atlassian.net/wiki/spaces/~311198967/pages/234848257/OpnSense+DynDNS+Setup](https://oga.atlassian.net/wiki/spaces/~311198967/pages/234848257 "https://oga.atlassian.net/wiki/spaces/~311198967/pages/234848257")

It’s a good guide on how to add a new VLAN and associated interface.

### Network Ports<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Here’s a picture of the backside of the router, showing ports and connections:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-03/scaled-1680-/5f9H6GnBGyiaEZAp-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-03/5f9H6GnBGyiaEZAp-image.png)

<div class="rich-media-item mediaSingleView-content-wrap image-center cc-1vkpcxj" data-layout="center" data-node-type="mediaSingle" data-renderer-start-pos="617" data-width="633" data-width-type="pixel" id="bkmrk--1"><div class="cc-1xy1ngf">  
</div></div>Here’s the list of used connections:

<div class="pm-table-container with-shadow-observer" data-layout="custom" id="bkmrk-logical-service-conn"><div class="pm-table-wrapper"><table data-layout="default" data-number-column="false" data-table-width="760" data-testid="renderer-table"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th aria-sort="none" class="ak-renderer-tableHeader-sortable-column__wrapper" colspan="1" data-colwidth="118" rowspan="1">**Logical**

</th><th aria-sort="none" class="ak-renderer-tableHeader-sortable-column__wrapper" colspan="1" data-colwidth="288" rowspan="1">**Service**

</th><th aria-sort="none" class="ak-renderer-tableHeader-sortable-column__wrapper" colspan="1" data-colwidth="351" rowspan="1">**Connection**

</th></tr><tr><td colspan="1" data-colwidth="118" rowspan="1">WAN (em0)

</td><td colspan="1" data-colwidth="288" rowspan="1">Incoming internet connection.

</td><td colspan="1" data-colwidth="351" rowspan="1">Connects directly to fiber transceiver

</td></tr><tr><td colspan="1" data-colwidth="118" rowspan="1">Spare (em1)

</td><td colspan="1" data-colwidth="288" rowspan="1">Spare port

</td><td colspan="1" data-colwidth="351" rowspan="1">NA

</td></tr><tr><td colspan="1" data-colwidth="118" rowspan="1">Spare (em2)

</td><td colspan="1" data-colwidth="288" rowspan="1">Spare port

</td><td colspan="1" data-colwidth="351" rowspan="1">NA

</td></tr><tr><td colspan="1" data-colwidth="118" rowspan="1">LAN (em3)

</td><td colspan="1" data-colwidth="288" rowspan="1">Untagged LAN traffic.

</td><td colspan="1" data-colwidth="351" rowspan="1">Connects to switch, SW20, port 8.

</td></tr><tr><td colspan="1" data-colwidth="118" rowspan="1">Trunk (em4)

</td><td colspan="1" data-colwidth="288" rowspan="1">VLan tagged traffic from main switch.

</td><td colspan="1" data-colwidth="351" rowspan="1">Connects to switch, SW20, port 1.

</td></tr><tr><td colspan="1" data-colwidth="118" rowspan="1">Mgmt (re0)

</td><td colspan="1" data-colwidth="288" rowspan="1">Management access from VLAN60.

</td><td colspan="1" data-colwidth="351" rowspan="1">Connects to switch, SW20, port 16.

</td></tr></tbody></table>

</div><div class="pm-table-sticky-scrollbar-container">  
</div></div>### Web UI Access<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

The UI is available on the LAN interface at: [https://192.168.1.1](https://192.168.1.1/ "https://192.168.1.1")

The LAN interface is LAN3 (em3), which is the bottom port on the 4-port NIC.

### Locked out of Web GUI<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

If you ever get locked out of the web interface, open an ssh session to the router, and issue this:

`configctl webgui restart renew`

Taken from here: [https://docs.opnsense.org/troubleshooting/webgui.html](https://docs.opnsense.org/troubleshooting/webgui.html "https://docs.opnsense.org/troubleshooting/webgui.html")

### Speed Test

Installed speed test plugin from here: [https://github.com/mimugmail/opn-repo](https://github.com/mimugmail/opn-repo)

This requires opening an SSH session to the router, and running this line (taken from the GitHub page):

```bash
fetch -o /usr/local/etc/pkg/repos/mimugmail.conf https://www.routerperformance.net/mimugmail.conf
pkg update
```

Once installed, open OpnSense and navigate to available plugins.

Locate the added plugin called: os-speedtest-community, and install it.

Once installed, you can open it from here:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-05/scaled-1680-/I07AtrsQCX8x5V00-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-05/I07AtrsQCX8x5V00-image.png)

# Supermicro Fan Problems

If you are running Noctua cooling fans in your PC and the PC is cycling fans up and down, then it’s probably because the motherboard thinks a fan has failed and is ramping them all up to compensate.

This is happening because Noctua fans run a lower RPM than other fan models.

If you are trying to fix this fan problem on an esxi host, you will need to install IPMITool from here: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://oga.atlassian.net/wiki/spaces/~311198967/pages/121339905" data-inline-card="true" data-renderer-start-pos="383"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[<span class="_o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="smart-link-title-wrapper">Installing IPMITool on ESXI</span></span>](https://oga.atlassian.net/wiki/spaces/~311198967/pages/121339905)</span></span></span>

This tutorial taken from here: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.informaticar.net/supermicro-motherboard-loud-fans/" data-inline-card="true" data-renderer-start-pos="418"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[<span class="_o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="smart-link-title-wrapper">Supermicro motherboard loud fans</span></span>](https://www.informaticar.net/supermicro-motherboard-loud-fans/)</span></span></span>

To fix this, requires editing the fan config via IPMITOOL.

Open a command line on your server, and download or install ipmitool:

```bash
apt-get install ipmitool
```

List the fans of the motherboard with:

```bash
ipmitool -I lan -U ADMIN -H 10.100.10.200 sensor
```

If you’re running on the actual host, you can just use this:

<div class="code-block  cc-1lw6ekp" id="bkmrk-"><div class="cc-1sws8jd"><div class="cc-1pxmft8"></div></div></div>```bash
ipmitool sensor
```

The listing will include all the relevant fan Ids the motherboard recognizes.

<div class="code-block  cc-1lw6ekp" id="bkmrk--1"><div class="cc-1sws8jd"><div class="cc-1pxmft8"><div role="presentation"></div></div></div></div>You will need to update the lower speed limits of each fan, with the following:

```bash
ipmitool -I lan -U ADMIN -H 10.100.10.200 sensor thresh FAN1 lower 150 225 300
```

Or, from the local host:

```bash
ipmitool sensor thresh FAN1 lower 150 225 300
```

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-04/scaled-1680-/dS4qb5b9BOBo7sPq-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-04/dS4qb5b9BOBo7sPq-image.png)

<div class="rich-media-item mediaSingleView-content-wrap image-center cc-6esmyc" data-layout="center" data-node-type="mediaSingle" data-renderer-start-pos="1061" data-width-type="percentage" id="bkmrk-repeat-the-above-com"><div class="cc-1npdknj"><div data-alt="" data-collection="contentId-98140161" data-context-id="98140161" data-file-mime-type="" data-file-name="file" data-file-size="1" data-height="134" data-id="fbd34b85-887e-44eb-94c0-6e1976c27de0" data-node-type="media" data-renderer-start-pos="1062" data-type="file" data-width="703">![](blob:https://oga.atlassian.net/911145dc-28fd-4252-a8a9-e5c8e429eb39#media-blob-url=true&id=fbd34b85-887e-44eb-94c0-6e1976c27de0&collection=contentId-98140161&contextId=98140161&width=703&height=134&alt=)Repeat the above command for each fan Id that shows up in the sensor listing.</div></div></div>The above command has these switches:

ADMIN – enter username of your IPMI interface

10.1550.20.200 – enter IP address of your IPMI interface

FAN1 – FAN for which you want to set values

150 – lower non-recoverable value

225 – lower critical value

300 – lower non-critical value

Usually, the change is immediate.  
But if not, shutdown the host, disconnect the power cord for a few minutes.  
Then, reboot. And, it should be corrected.

# Supermicro Fan Scripting

Some references:

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.truenas.com/community/threads/script-hybrid-cpu-hd-fan-zone-controller.46159/" data-inline-card="true" data-renderer-start-pos="19"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[https://www.truenas.com/community/threads/script-hybrid-cpu-hd-fan-zone-controller.46159/](https://www.truenas.com/community/threads/script-hybrid-cpu-hd-fan-zone-controller.46159/)</span></span></span>

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.truenas.com/community/threads/script-hybrid-cpu-hd-fan-zone-controller.46159/" data-inline-card="true" data-renderer-start-pos="19"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[https://www.truenas.com/community/threads/script-to-control-fan-speed-in-response-to-hard-drive-temperatures.41294/](https://www.truenas.com/community/threads/script-to-control-fan-speed-in-response-to-hard-drive-temperatures.41294/)</span></span></span>

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.truenas.com/community/threads/script-hybrid-cpu-hd-fan-zone-controller.46159/" data-inline-card="true" data-renderer-start-pos="19"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[https://forums.servethehome.com/index.php?resources/supermicro-x9-x10-x11-fan-speed-control.20/](https://forums.servethehome.com/index.php?resources/supermicro-x9-x10-x11-fan-speed-control.20/)</span></span></span>

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.truenas.com/community/threads/script-hybrid-cpu-hd-fan-zone-controller.46159/" data-inline-card="true" data-renderer-start-pos="19"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[https://www.mikesbytes.org/server/2019/03/01/ipmi-fan-control.html](https://www.mikesbytes.org/server/2019/03/01/ipmi-fan-control.html)</span></span></span>

# HowTo Retrieve Key and Cert from PFX

Here’s a good article on how to export SSL certificates, extract the key and certificate, and import it into AWS.

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.bluelabellabs.com/blog/how-to-export-certificates-from-windows-for-use-in-apache-nginx-on-linux/" data-inline-card="true" data-renderer-start-pos="116"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[<span class="_o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="smart-link-title-wrapper">How To Convert Windows PFX Certificate Files Into PEM Format On Linux</span></span>](https://www.bluelabellabs.com/blog/how-to-export-certificates-from-windows-for-use-in-apache-nginx-on-linux/)</span></span></span>

The above article uses openSSL, which can be found for Windows, here:

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://slproweb.com/products/Win32OpenSSL.html" data-inline-card="true" data-renderer-start-pos="192"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper" role="button">[<span class="_o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="smart-link-title-wrapper">Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions</span></span>](https://slproweb.com/products/Win32OpenSSL.html)</span></span></span>

As well, the above article has an error in Step 4, that removes the password from the key.pem to create the raw private key file.

That command should be:

```bash
openssl rsa -in key.pem -out server.key
```

# Linux Disk Usage

Linux has a few means to analyze a disk for space usage.

One way, is a package called, ncdu.

It can be downloaded with:

```bash
sudo apt install ncdu
```

You execute it, like this:

```bash
sudo ncdu -x /
```

NOTE: -x forces it to stay within the same filesystem, and not traverse other mounts.

You can add the -r switch, to make it read-only.

When run, it will iterate the filesystem, and create an interactive usage list, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-04/scaled-1680-/Qi0OTZifVQdvIi0o-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-04/Qi0OTZifVQdvIi0o-image.png)

#### Clearing Tmp Folder

Ansible likes to push container images and other things into the /tmp folder of its host.

Here’s a quick command to clear temp files that are older than 10 days:

```bash
sudo find /tmp -type f -atime +10 -delete
```

# Creating SSH Keys in Linux

Creating an SSH key is straightforward on a linux client, using this command:

```bash
ssh-keygen
```

By default recent versions of `ssh-keygen` will create a 3072-bit RSA key pair, which is secure enough for most use cases (you may optionally pass in the `-b 4096` flag to create a larger 4096-bit key).

<p class="callout info">NOTE: See this page for naming conventions, before actually creating a new key: [SSH Key Naming Convention](https://wiki.galaxydump.com/link/386)</p>

Also. The command allows you to specify the key type (-t), key size (-b), key comment (-C), and output filename (-f) like this:

```bash
ssh-keygen -t rsa -b 4096 -C "rsa4096-glwhite-hadron-20230913" -f ./rsa4096-glwhite-hadron-20230913.key
```

Here are some different type and size combinations:

```bash
# Create an RSA key with key size of 4096 bits...
ssh-keygen -t rsa -b 4096

# Create a DSA key...
# This is an old government key type (Digital Signature Algorithm), defaulting to a 1024 bit size.
ssh-keygen -t dsa 

# Create an ECDSA521 key...
ssh-keygen -t ecdsa -b 521

# Create an ED25519 key...
# NOTE: This type is not widely accepted, yet.
ssh-keygen -t ed25519
```

If you ran the command without specifying a filename (-f), it will prompt you for the output location:

```bash
OutputGenerating public/private rsa key pair.
Enter file in which to save the key (/your_home/.ssh/id_rsa):
```

You can accept the default path (pressing enter), to save the key pair into the `.ssh/` subdirectory in your home directory, or specify an alternate path.

If a key already exists at the path, you may see the following prompt:

```bash
Output/home/your_home/.ssh/id_rsa already exists.
Overwrite (y/n)?
```

<p class="callout warning">WARNING: If you choose to overwrite the key on disk, you will **not** be able to authenticate using the previous key anymore. Be very careful when selecting yes, as this is a destructive process that cannot be reversed.</p>

You should then see the following prompt:

```bash
OutputEnter passphrase (empty for no passphrase):
```

Here you optionally may enter a secure passphrase, which is highly recommended. A passphrase adds an additional layer of security to prevent unauthorized users from logging in. To learn more about security, consult our tutorial on [How To Configure SSH Key-Based Authentication on a Linux Server](https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server "https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server").

You should then see the output similar to the following:

```
OutputYour identification has been saved in /your_home/.ssh/id_rsa
Your public key has been saved in /your_home/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:/hk7MJ5n5aiqdfTVUZr+2Qt+qCiS7BIm5Iv0dxrc3ks user@host
The key's randomart image is:
+---[RSA 3072]----+
|                .|
|               + |
|              +  |
| .           o . |
|o       S   . o  |
| + o. .oo. ..  .o|
|o = oooooEo+ ...o|
|.. o *o+=.*+o....|
|    =+=ooB=o.... |
+----[SHA256]-----+
```

# Kali in VMWare

Here are some things for installing Kali in a VMWare VM.

Adapted from here: [https://www.kali.org/docs/virtualization/install-vmware-guest-vm/](https://www.kali.org/docs/virtualization/install-vmware-guest-vm/ "https://www.kali.org/docs/virtualization/install-vmware-guest-vm/")

Choose the latest Workstation Hardware version. 16.x is current as of this writing.

Kali is debian based, so use the latest version Debian HAL.

Give your VM at least two processors and two cores per processor (4 cores minimum).  
Give it at least 4GB.

The tutorial recommend using NAT for the NIC. Go with this, until we need bridged, in case there’s a subtlety of NAT that’s beneficial.

Accept the LSI and SCSI disk defaults, and choose at least 40GB size.

Once created, open the Edit VM Settings, and find the USB controller.  
Disable the Automatically Connect new USB devices, so that you will be prompted for each one, as needed.

On the Display pane, be sure to disable Accelerate 3D graphics, as this causes trouble for some people.

On the Power tab, enable the Report Battery Information to Guest, as this is helpful, so you get notifications when the host is out of juice.

And, enable time sync with the host, so you don’t have to set the clock for updates to work.

# Create PFX Cert File (for IIS)

Windows IIS requires a pfx file when importing an SSL certificate.

This can be created from a crt and a key file using openssl and the following steps.

Locate the openssl.exe on your system. It is usually installed as part of git, and located, here:  
C:\\Program Files\\Git\\usr\\bin\\openssl.exe

Open a command line and navigate to the folder where your crt and private key file (key) are stored.

Execute a command of the following form, to create the pfx.

NOTE: You will need to replace elements with your specific key names:

```bash
openssl pkcs12 -export -out new-pfx-cert.pfx -inkey private-key.key -in certificate.crt
```

For example, when using the openssl binary and a specific site’s key, the statement looks like this:

```bash
"C:\Program Files\Git\usr\bin\openssl.exe" pkcs12 -export -out STAR.ogsofttech.com.pfx -inkey STAR.ogsofttech.com.key -in STAR.ogsofttech.com.crt
```

You will be prompted for the password of the key file.

<p class="callout info">NOTE: The pfx will be generated without error, regardless if the entered key password was correct or not.</p>

Windows IIS will confirm the password on import, and fail if incorrect.

# Docker Space Full

Here are some things to check on when the disk on a docker host gets full.

```
sudo docker system prune -a -f
```

This will cleanup other resource types:

```bash
docker system prune -a
```

This command will remove older logs from the container logs folder:

```bash
sudo find /projects/container_logs/ -type f -mtime +7 -name '*.log' -exec rm {} \;
```

This command will remove logs from the container folder:

```bash
sudo find /var/lib/docker/containers/ -name "*-json.log" -exec truncate -s 0 {} \;
```

This command will remove older logs from the /var/log/oga folder:  
This takes care of logs generated by naked services.

```bash
sudo find /var/log/oga/ -type f -mtime +7 -name '*.log' -exec rm {} \;
```

This command will remove older logs from the /var/log/bliss folder:  
This takes care of logs generated by naked services.

```bash
sudo find /var/log/bliss/ -type f -mtime +7 -name '*.log' -exec rm {} \;
```

# Windows



# Validating AD Machine Account

Here's a couple ways to validate the machine account of a PC in an Active Directory domain:

```
dsregcmd /status
```

If successful, it will output a device state of DomainJoined: yes.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/42OdEyuusCj3aEV3-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/42OdEyuusCj3aEV3-image.png)

NOTE: It outputs several blocks of info. So, you may have to scroll up to see the status.

Also. You can use this:

```
nltest /sc_query:<<domain name>>
```

If successful, it will give a NERR\_Success status, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/CKNKbSm6W1Vcdo73-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/CKNKbSm6W1Vcdo73-image.png)

# Generate SSL Cert

Here’s some steps on how to generate and setup an SSL certificate for a web site.

1. First, order a certificate from a CA, like Sectigo, or Digicert.
2. Once your order is active, they will require you to submit a CSR.  
    This must be generated by you, and will output two things.  
    It will generate your private key (for all SSL traffic). And, it will generate the CSR that contains your public key to be signed by the CA.
3. To generate a CSR is pretty easy. There’s a wizard tool, here, that will give you the command line string that you can execute on a linux box using openssl.  
    <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.digicert.com/easy-csr/openssl.htm" data-inline-card="true" data-renderer-start-pos="580" data-ssr-placeholder="0vDZ-:EfLS5:URVzo:4y5Pz:qz-Pe:F4Zdx-0"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">OpenSSL CSR Tool - Create Your CSR Faster | DigiCert.com</span></span>](https://www.digicert.com/easy-csr/openssl.htm)</span></span>

The CSR wizard looks like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/3HYZD6jQW3RP9TD5-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/3HYZD6jQW3RP9TD5-image.png)

Here’s what the CSR wizard looks like for a wildcard domain:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/ttBltGWIPKWLBuA5-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/ttBltGWIPKWLBuA5-image.png)

For the wildcard domain, the tool generates this output:

```bash
openssl req -new -newkey rsa:4096 -nodes -out star_ogsofttech_com.csr -keyout star_ogsofttech_com.key
-subj "/C=US/ST=Nc/L=Morrisville/O=OG Automation LLC/OU=Engineering/CN=*.ogsofttech.com"
```

<p class="callout info">NOTE: The above is listed on multiple lines ONLY for display in this article. For usage, it must entered all on one command line.</p>

Fill out the fields in the CSR request tool, and the right-hand pane will be what you execute as a shell statement in linux.

4. Open a linux shell, and execute the generated command, to create your private SSL key and CSR, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/DC6XOhv8Oebrtqdo-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/DC6XOhv8Oebrtqdo-image.png)

<p class="callout info">NOTE: The second statement lists the folder contents, showing the generated key and CSR files.</p>

5. Take your SSL key and put it in a safe place, until ready to deploy it to your website.

<p class="callout info">NOTE: We’ve had some trouble with the Sectigo validation flow, in that the validation will report failure (on the web page), when the certificate actually got issued.</p>

So, don’t discard the private key you created, until you’ve confirmed via email that your CSR failed validation.  
On a previous occasion, we saw the validation fail in the UI, and deleted the private key, to redo the CSR submission. But, it had actually passed and the certificate was issued… but the key was then deleted.  
So, keep all versions until you have a validated key-crt pair.

6. Open the CSR file. It should look something like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/k1IpKkgo4jsxD0Lf-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/k1IpKkgo4jsxD0Lf-image.png)

Paste its contents into a notepad session, so you can easily submit it to the CA website.

7. Go back to your CA website’s setup flow, and submit your CSR.  
    Once submitted, your CA will require some form of proof (validation) that you own the domain they’re creating the SSL key for. This will be via email reply, a custom DNS entry, or a file to expose on your website.
8. Go through the validation of that, and your CA will send you your signed SSL certificate and bundle.

For example: The validation flow with Sectigo has an option for DNS registration.  
To use it, you must log into your domain registrar, and add a special CNAME record to your domain, that Sectigo will use to verify that you own the domain being registered.

A CNAME DNS entry for validating domain ownership with Sectigo looks like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/640DYpG2i2h3SuDb-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/640DYpG2i2h3SuDb-image.png)

<p class="callout info">NOTE: It’s not clear if the Name field (of the DNS record) should include the domain at the end of the hash string. So, we added two entries; one with, and one without.</p>

9. Once the validation steps have passed, the CA will issue your certificate (crt file) and a ca bundle.
10. Download the crt and bundle files from your CA.  
    Now, you should have a key file (the private key you created with your CSR), a crt file (your public certificate), and your CA should have given you a CA bundle file as well.
11. For Nginx to use your certificate, it must be chained with the CA bundle.  
    This is a simple concatenation operation. To so, make sure both crt and CA bundle files are in the same folder, and execute the following:
12. To make one, run the following command:

```bash
$ cat www.sitecertificatefile.com.crt intermediatebundle.crt > www.example.com.chained.crt
```

The above command takes the site certificate file and adds on the intermediate certificate, putting both into a composite file, called a chained certificate.

<p class="callout info">NOTE: The order of concatenation is important in the above command, as NGINX will consider the first certificate in the chained file as your SSL certificate.</p>

When NGINX starts up, it will attempt to match the key and first certificate in the chained file. If they don’t match, it will give you an error like this:

```bash
SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
   (SSL: error:0B080074:x509 certificate routines:
    X509_check_private_key:key values mismatch)
```

This chained certificate is what needs to be pushed into the NGINX server configuration.

Refer to this: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://nginx.org/en/docs/http/configuring_https_servers.html" data-inline-card="true" data-renderer-start-pos="4527" data-ssr-placeholder="0vDZ-:EfLS5:URVzo:4y5Pz:qz-Pe:F4Zdx-1"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Configuring HTTPS servers</span></span>](https://nginx.org/en/docs/http/configuring_https_servers.html)</span></span>

12. Move the key and chained crt files to your Nginx host, and configure Nginx to use them.

# Converting PEM to crt and key

If you receive pem formatted certificate and key files from a CA registrar, you will need to convert them for use by a linux host.

## Bundles and Chains<button aria-hidden="true" class="cc-wf6gg8" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

As well, it may be necessary to compose a certificate chain file, for the host, instead of a simple crt file.  
Doing so, is required for Nginx and some mobile apps, as both require the CA authority certs to be in the same file as the host certificate.  
To create a chain certificate, do this:

```bash
cat cert-start.pem ca-bundle.pem > full_chain.pem
```

## Pem to Crt<button aria-hidden="true" class="cc-wf6gg8" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

To convert the cert file (from pem) to crt, do this:

```bash
openssl x509 -outform der -in your-cert.pem -out your-cert.crt
```

## Pem to Key<button aria-hidden="true" class="cc-wf6gg8" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

To convert the private key (as pem) to key, do this:

```bash
openssl rsa -outform der -in private.pem -out private.key
```

# Convert SSL PFX for NGINX Usage

NGINX doesn’t natively use a pfx key file (pfx is what Windows IIS needs). So, it must be converted to a private key, removing the public key from it.

Create the folder for storing SSL certificates:

```bash
cd /etc/nginx/
mkdir ssl
cd ssl
chmod 700 /etc/nginx/ssl
```

From the pfx file, recover the public certificate:

```bash
openssl pkcs12 -in cert.pfx -clcerts -nokeys -out public.crt
```

From the pfx file, recover the encrypted private key:

```bash
openssl pkcs12 -in cert.pfx -nocerts -nodes -out private.rsa
```

Now, decrypt the encrypted private key:

```bash
openssl rsa -in private.rsa -out private.key
```

Move the public certificate and private key to the ssl folder, created earlier.

Set permissions on the ssl folder and files, so only root can access the certs and keys:

```bash
chmod 600 -R /etc/nginx/ssl/*
```

# NTP Server

Here are some useful commands when running a GPS-disciplined NTP server.

See this page for the local NTP server (192.168.1.12): [Local GPS NTP Time Server](https://wiki.galaxydump.com/link/456)

## NTPQ

When running a query of an ntp server, you would use: ntpq -p.  
What you see is this:  
ntpq -pn

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/ArHhqTNBggVxixRX-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/ArHhqTNBggVxixRX-image.png)

Here’s an explanation:

REMOTE = The servers and peers specified in the configuration file, from which your host will take time synchronization

Character that may prefix the remote hostname/IP address:

- indicates the current synchronization source.

\# indicates that the host is selected for synchronization, but distance from the host to the server exceeds the maximum value.

o indicates that the host is selected for synchronization and the PPS signal is in use.

\+ indicates the host is included in the final synchronization selection set.

x indicates that the host is the designated false ticker by the intersection algorithm.

. indicates that the host is selected from the end of the candidate list.

– indicates a host discarded by the clustering algorithm.

blank indicates a host is discarded due to high stratum and/or failed sanity checks.

REFID = the current source of synchronization for the remote host

ST = the stratum level of the remote host

T = types available:

l local (such as a GPS clock)  
u unicast (this is the common type)  
m multicast  
b broadcast  
– netaddr (usually 0)

WHEN = number of seconds passed since the remote host response

POLL = polling interval to the remote host, defined with the “minpoll” value in ntp.conf file

REACH = indicates how successful attempts to reach the server are. This is an 8-bit shift register with the most recent probe in the 2^0 position. The value 001 indicates the most recent probe was answered, while 357 indicates one probe was not answered. The value 377 indicates that all the recent probes have been answered.

DELAY = (round trip time) indicates the time (in milliseconds) taken by the reply packet to return in response, to a query sent by the server.

OFFSET = indicates the time difference (in milliseconds) between the server’s clock and the client’s clock. When this number exceeds 128, and the message synchronization lost appears in the log file

JITTER = indicates the difference in the offset measurement between two samples. This is an error-bound estimate. Jitter is a primary measure of the network service quality.

## NTPTIME

This command will give you the current time as:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/xVlnkwmkMhMwUe8B-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/xVlnkwmkMhMwUe8B-image.png)

## GPSMON

This is used to get access to a semi-graphical display of raw GPS location and time data.

The output looks like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/kL8zpqUyEWjFr6sM-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/kL8zpqUyEWjFr6sM-image.png)

# WSL Setup

In case it’s necessary to use Windows Subsystem for Linux, here’s a good tutorial on how to get it running:

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://docs.microsoft.com/en-us/windows/wsl/install" data-inline-card="true" data-renderer-start-pos="110"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Install WSL</span></span>](https://docs.microsoft.com/en-us/windows/wsl/install)</span></span></span>

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://docs.microsoft.com/en-us/windows/wsl/setup/environment#set-up-your-linux-username-and-password" data-inline-card="true" data-renderer-start-pos="114"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Set up a WSL development environment</span></span>](https://docs.microsoft.com/en-us/windows/wsl/setup/environment#set-up-your-linux-username-and-password)</span></span></span>

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://ubuntu.com/blog/creating-cross-platform-applications-with-net-on-ubuntu-on-wsl" data-inline-card="true" data-renderer-start-pos="118"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Getting started with cross-platform development using .NET on Ubuntu on WSL | Ubuntu</span></span>](https://ubuntu.com/blog/creating-cross-platform-applications-with-net-on-ubuntu-on-wsl)</span></span></span>

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://blog.ndepend.com/debugging-a-net-app-on-linux-from-windows-visual-studio-with-wsl/" data-inline-card="true" data-renderer-start-pos="122"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Debugging a .NET App on Linux from Windows Visual Studio with WSL - NDepend Blog</span></span>](https://blog.ndepend.com/debugging-a-net-app-on-linux-from-windows-visual-studio-with-wsl/)</span></span></span>

# RunDeck Backups

Adapted from here: [https://docs.rundeck.com/docs/administration/maintenance/backup.html](https://docs.rundeck.com/docs/administration/maintenance/backup.html)

We will use the rd tool to access the RunDeck API and export the project job list.

We will be backing things up to this folder:

```
/projects/rundeck_backups
```

1\. Execute this, to export the project and job list:

```bash
rd jobs list -f /projects/rundeck_backups/jobs.xml -p ansible-test
```

If the above command returns this error, you need to set the environment variable:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/scaled-1680-/2FElW4msrwbYHGvu-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-06/2FElW4msrwbYHGvu-image.png)

If so, set this variable:

```
export RD_URL=http://192.168.1.200:4440
```

2\. Stop the server:

```bash
sudo systemctl stop rundeckd.service
```

3\. Backup the database:

```
sudo cp -r /var/lib/rundeck/data /projects/rundeck_backups/data/
```

4\. Copy the logs:

```bash
sudo cp -r /var/lib/rundeck/logs /projects/rundeck_backups/logs/
```

5\. Start the server:

```bash
sudo systemctl start rundeckd.service
```

# Ubuntu Service Creation (Systemd)

Below are instructions on how to setup a binary as a linux service.

For additional systemd commands, see this: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://oga.atlassian.net/wiki/spaces/~311198967/pages/89522177" data-inline-card="true" data-renderer-start-pos="113" data-ssr-placeholder="0vDZ-:EfLS5:4y5Pz:qz-Pe:F4Zdx-0"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">SystemCtl Usage</span></span>](https://oga.atlassian.net/wiki/spaces/~311198967/pages/89522177)</span></span>

### Service Creation

Here are steps to run a binary as a linux service.

Create a systemd unit file for the service by generating a file in: /lib/systemd/system  
The file should contain these things:

```ini
[Unit]
Description=OGA.HostControl.Service
After=network.target

[Service]
ExecStart=/usr/bin/bliss/oga.hostcontrol.service/OGA.HostControl.Service --urls "http://192.168.1.201:4180;http://172.17.0.1:4180"
Environment=DOTNET_CLI_HOME=/temp
WorkingDirectory=/usr/bin/bliss/oga.hostcontrol.service
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

<p class="callout info">NOTE: The above systemd unit file was generated for a dotnet service, called: OGA.HostControl.Service.</p>

<p class="callout info">NOTE: This service runs as root, as no user was specified in the \[Service\] section.</p>

<p class="callout info">NOTE: We have specified a working directory that points back to the bin folder of the service. This is necessary for a dotnet service.</p>

Once the unit file has been created for the service, you must tell systemd to load it.  
Tell systemd to reload unit files with this:

```bash
sudo systemctl daemon-reload
```

With the unit file available, you should be able to start the service.

Start the service with this:

```bash
sudo systemctl start servicename
```

Verify the service is running with this:

```bash
sudo systemctl status servicename
```

Once happy that the service is configured correctly and will run as required, you should enable it to start on boot.

To enable a service to start on boot, do this:

```bash
sudo systemctl enable servicename
```

At this point, your service will startup each time the machine reboots.

# SystemCtl Usage

Below is a list of commonly used systemd commands.

If you are needing to configure a binary as a linux service, see this: [Ubuntu Service Creation](https://wiki.galaxydump.com/link/368)

### Reload Systemd Units

Each time you edit a systemd unit file, you must tell systemd to reload changes.  
To reload all systemd unit config files:

```bash
sudo systemctl daemon-reload
```

### Console Logs

Systemd logs all console output from running services.  
To get console logs from a service, do this:

```bash
sudo journalctl -u nginx.service
```

### Reload Unit Files

Tell systemd to reload unit files with this:

```bash
sudo systemctl daemon-reload
```

### Start a Service

Start the service with this:

```bash
sudo systemctl start servicename
```

### Check Service Status

Verify the service is running with this:

```bash
sudo systemctl status servicename
```

### Enable Service Start on Boot

To enable a service to start on boot, do this:

```bash
sudo systemctl enable servicename
```

# UFW and Docker

When running containers in Docker, you will come across the need for a container to gain access to a resource on the host.  
If you have UFW firewall enabled, you will need to allow ingress from the docker network, running on the host.

To identify the docker network, run this:

```bash
sudo docker network ls
```

It will return the list of docker networks:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-07/scaled-1680-/3SKZ7Xf378xvxbjs-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-07/3SKZ7Xf378xvxbjs-image.png)

Next, you need to identify the docker network where your containers are addressed.  
To do this, run the following on each network name:

```bash
sudo docker network inspect bridge
```

The above command will give you a list of containers and their addresses and subnets.

Next, you will update the ufw firewall rules to allow incoming connection from the docker subnet, like this:

```bash
sudo ufw allow from 172.17.0.0/16
```

You can check the status and rules with:

```bash
sudo ufw status
```

This will return something like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-07/scaled-1680-/wwsZrQWnOpr4Gj90-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-07/wwsZrQWnOpr4Gj90-image.png)

Now, you should be able to access host resources from a docker container, with UFW enabled.

# Docker Commands

Here’s a list of commands to remember for docker administration.

#### List Containers

To list all docker containers on a host:

```bash
sudo docker ps -a
```

#### Remove Containers

To remove all docker containers on a host:

```bash
sudo docker rm -f $(sudo docker ps -aq)
```

#### Container Logs

To see the logs for a container:

```bash
sudo docker logs containername
```

#### Container Stats

To get the stats of docker containers, as a single snapshot:

```bash
docker stats --no-stream
```

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.digitalocean.com/community/tutorials/how-to-remove-docker-images-containers-and-volumes" data-inline-card="true" data-renderer-start-pos="369"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">Additional commands are here: </span></span></span><span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.digitalocean.com/community/tutorials/how-to-remove-docker-images-containers-and-volumes" data-inline-card="true" data-renderer-start-pos="369"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">How To Remove Docker Images, Containers, and Volumes | DigitalOcean</span></span>](https://www.digitalocean.com/community/tutorials/how-to-remove-docker-images-containers-and-volumes)</span></span></span>

#### Copy Files from Container

To copy files out of a docker container, do this:

```bash
sudo docker cp <imageid>:/pathincontainer ~/pathonhost
```

#### Running a Terminal Container

Sometimes, it’s necessary to spin up a blank docker container for testing network or other facilities from a terminal session in the container.

Here’s a quick command that will spin up a container with an ubuntu bash terminal.

<p class="callout info">NOTE: It also adds an /etc/hosts entry of (host.docker.internal) which is the docker entry for accessing the container’s host machine.</p>

This is useful if you are testing visibility of any host services from the container.

```bash
docker run --rm -it --add-host host.docker.internal:host-gateway --entrypoint bash ubuntu
```

Once started and at the terminal, you can install network utilities such as ping, curl, etc with this:

```bash
apt-get update; apt-get install curl; apt-get install inetutils-ping; apt-get install net-tools
```

#### Opening a Terminal Inside An Active Container

If you want to open a terminal, inside an already running container, you can use this command:

```bash
docker exec -it <container_id_or_name> /bin/bash
```

If the container doesn't have bash, use this:

```bash
docker exec -it <container_id_or_name> /bin/sh
```

As well, you can install network utilities, such as Ping, curl, etc, into the container while in its terminal:

```
apt-get update; apt-get install curl; apt-get install inetutils-ping; apt-get install net-tools
```

#### Get Docker Gateway Address

Here’s a pair of commands to get the docker’s bridge network gateway address and subnet:

```bash
docker network inspect bridge --format='{{(index .IPAM.Config 0).Gateway}}'

docker network inspect bridge --format='{{(index .IPAM.Config 0).Subnet}}'
```

# SSH Keys



# SSH Key Naming Convention

This page describes a good naming convention for SSH keys, that makes them easier to track, rotate, and revoke.

You should use this naming convention for the filename of keys.  
And as well, use the same convention when populating the the comment field of each key.

Here are some design choices for SSH key naming:

- SSH keys are a client-centric object. So, the name should be client-centric.  
    Specifically, the name should include the username (actual user or service name).
- To more easily track key usage, the name should include the client machine, where it is installed.
- To know how old a key is (for expiry purposes), the name should include the creation date.
- Since key algorithms can become compromised or superceded by newer ones, some distros and services will refuse certain key types.  
    So, we will include the key algo in the name.

The composite key name convention becomes this:

&lt;keytype&gt;-&lt;username&gt;-&lt;client&gt;-&lt;date&gt;

Here is an example of this key naming:

RSA2048-glwhite-hadron-20220428

The above key name expression has the following terms:

1. Key type - what encryption type was used and key strength
2. User Name - associates a key to a single user, for proper authentication and access authorization
3. Client device - associates a key to a device, so the key can be revoked if the device is compromised
4. Creation timestamp - marks when the key was created, so older versions can be identified and revoked

Following this convention allows use to easily identify keys that need to be revoked. As we can revoke keys by device, by user, or by type as a particular encryption becomes obsolete.

As well, the creation time allows us to know when to periodically rotate keys, to limit risk.

The key name should be used as the key comment at the end of a key string in an SSH key file on a server.

The key name should be part of both the private and public key file names (ppk files) on a client machine.

The key name can be set as the Key Comment when generating a key, using PuttyGen, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-08/scaled-1680-/SftOFiuEUQpMNLEP-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-08/SftOFiuEUQpMNLEP-image.png)

When PuttyGen creates a key, both the private key and public keys should be saved to disk.

A password can be added to the private key, from within PuttyGen, to prevent unauthorized usage.

If the private key is saved without a password, be sure to store it in a safe bag or in an encrypted volume.

The public key can be stored and distributed without concern.

<p class="callout info">NOTE: We’ve purposely kept the creation time as the last term. This ensures that any automation used to rotate keys can successfully identify the timestamp for all keys (by looking at the last term).</p>

#### Definite Purpose Keys

There are scenarios that require an ssh key for a definite purpose, such as authentication to a service, such as Github.

In this scenario, it is wise to include the service as a term in the ssh key name and key comment.

Doing so, extends the key naming convention to:

&lt;keytype&gt;-&lt;username&gt;-&lt;client&gt;-&lt;purpose&gt;-&lt;date&gt;

Here is an example of this key naming:

RSA2048-glwhite-hadron-github-20220428

# How to Get Host SSH Key Fingerprints

Here’s a short command line statement that will fetch the host ssh key fingerpring without authenticating with it:

```bash
ssh-keyscan host | ssh-keygen -lf -
```

Here’s another way to clean up ssh host key fingerprints:

```bash
# remove any old fingerprints for the host
ssh-keygen -R server.example.com
# add SSH fingerprints for the host
ssh-keyscan -t ecdsa,ed25519 -H server.example.com >> ~/.ssh/known_hosts 2>&1
```

# Creating SSH Keys in Windows

## General Notes<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

SSH keys can be easily generated in Windows, using PuttyGen.

See this article for an update based on obsolete SHA-1 RSA key usage: [Ubuntu 22.04 SSH the RSA key isn't working since upgrading from 20.04](https://askubuntu.com/questions/1409105/ubuntu-22-04-ssh-the-rsa-key-isnt-working-since-upgrading-from-20-04 "https://askubuntu.com/questions/1409105/ubuntu-22-04-ssh-the-rsa-key-isnt-working-since-upgrading-from-20-04")

Based on the obsolescence of RSA keys in Ubuntu, it is advised to use ECDSA keys instead.

## Needed Tooling<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

This tutorial uses PuttyGen to create SSH keys in Windows. So, download and install it if needed.  
PuttyGen can be downloaded, here: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://www.puttygen.com/" data-inline-card="true" data-renderer-start-pos="458"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">PuTTYgen Download</span></span>](https://www.puttygen.com/)</span></span></span>

## Good SSH Key Conventions<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

Here are some good conventions to follow for key security and easy maintenance:

### User Keys<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

Each user should have their own SSH keys. No sharing for obvious reasons.

If a user is fired or leaves, only the keys identified for that user need to be revoked.

Again. No sharing keys between users.

### Client Devices<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

An SSH key should be created for each client device that a user connects from.

Having a unique set of keys for each client device compartmentalizes the risk of a lost or stolen device. Specifically, when a user’s laptop or phone is lost or compromised, only the SSH keys on that device need to be revoked. Other keys for that user remain unaffected.

Don’t use ssh keys creation for one device on another device. Don’t share them between clients!  
Sharing keys across clients does several things:

- It prevents the remote endpoint from accurately identifying the client.
- If a client is compromised, it is harder to identify and isolate the keys that need to be revoked for the compromised client.

### SSH Key Naming<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

See this page for naming convention: [SSH Key Naming Convention](https://wiki.galaxydump.com/link/386 "https://wiki.galaxydump.com/link/386")

# Authenticating to Linux Server with SSH Keys

Once you have configured the server with SSH key authentication, you can follow this to attempt connection.

<p class="callout info">NOTE: Using SSH key authentication does not require a password for the remote account.</p>

From a Linux host, use this:

```bash
ssh username@remote_host
```

If this is your first time connecting to this host (if you used the last method above), you may see something like this:

```bash
OutputThe authenticity of host '203.0.113.1 (203.0.113.1)' can't be established.
ECDSA key fingerprint is fd:fd:d4:f9:77:fe:73:84:e1:55:00:ad:d6:6d:22:fe.
Are you sure you want to continue connecting (yes/no)? yes
```

This means that your local computer does not recognize the remote host. Type “yes” and then press `ENTER` to continue.

If you did not supply a passphrase for your private key, you will be logged in immediately.

If you supplied a passphrase for the private key when you created the key, you will be prompted to enter it now.

<p class="callout info">NOTE: your keystrokes will not display in the terminal session for security.</p>

After authenticating, a new shell session should open for you with the configured account on the remote server.

# Linux: Disabling Password Authentication

Once you have confirmed that you have SSH key access to a Linux host (using SSH without a password), it is safe to disable password-based authentication.

<p class="callout warning">WARNING: This step will lock down password-based logins, so ensuring that you will still be able to get administrative access is crucial.</p>

<p class="callout warning">Before proceeding, make sure that you do, indeed, have SSH-key authentication access configured for at least one user with sudo privileges.</p>

<p class="callout info">NOTE: Best practice is that the configured administrative user is not actually the root user.  
But, is a non-root user, with sudo privileges.</p>

<p class="callout info">**Note:** If you are setting up a DigitalOcean VM, and provided an SSH key when creating a droplet, password authentication may have been automatically disabled. You can still verify this by reading on.</p>

Once you’ve confirmed that your remote account has administrative privileges (has sudo access), log into your remote server with SSH keys.

Then, open up the SSH daemon’s configuration file:

```bash
sudo nano /etc/ssh/sshd_config
```

Inside the SSH config file, search for a directive called `PasswordAuthentication`.  
This line may be commented out with a `#` at the beginning of the line.

Uncomment the line by removing the `#`, and set the value to `no`.  
This will disable your ability to log in via SSH using account passwords:

```bash
. . .
PasswordAuthentication no
. . .
```

Save and close the config file when you are finished by pressing `CTRL+X`, then `Y` to confirm saving the file.

To actually activate the updated SSH config changes, we need to restart the `sshd` service:

```bash
sudo systemctl restart ssh
```

As a precaution, open up a new terminal window and test that the SSH service is functioning correctly before closing your current session:

```bash
ssh username@remote_host
```

Once you have verified your SSH service is functioning properly, you can safely close all current server sessions.

The SSH daemon on your Ubuntu server now only responds to SSH-key-based authentication.

Password-based logins have been disabled.

# Adding SSH Keys with ssh-copy-id

If you are attempting to add an SSH key (to a Linux host), from another Linux host, you can use a built-in utility called, `ssh-copy-id` .

The `ssh-copy-id` tool is included by default in many operating systems, so you may have it available on your local system. For this method to work, you must already have password-based SSH access to your server.

Due to its simplicity, this method is highly recommended if available. If you do not have `ssh-copy-id` available to you on your client machine, you may use one of the two alternate methods provided in this section (copying via password-based SSH, or manually copying the key).

<p class="callout info">NOTE: Using ssh-copy-id requires password authentication enabled.  
See the bottom of this page for how to temporarily enable password auth on the remote host.</p>

#### Requires Password Auth<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

This utility is easy to use, before disabling password authentication.  
But, it does encounter issues, trying to copy a public key to a remote host, while simultaneously logging into it with a different key.  
That quite often fails.  
So, first, we will ensure that password authentication is enabled on the remote host.

Log into the remote host, and open the sshd\_config file, located at: /etc/ssh/sshd\_config.

Locate the line with 'PasswordAuthentication', and set it to yes, like this:

```bash
...
PasswordAuthentication yes
...
```

Save and close the config file.

Restart the ssh service, with this:

```bash
sudo systemctl restart ssh
```

Now, you can use the utility from the local linux VM.

#### SSH-Copy-Id Usage

To use the utility, you specify the remote host that you would like to connect to, and the user account that you have password-based SSH access to.

<p class="callout info">NOTE: The account you log in to the local Linux host with, will also be the account, whose public SSH key will be pushed to the remove Linux host. So, this will be the account to which your public SSH key will be copied.</p>

The abbreviated syntax is:

```bash
ssh-copy-id username@remote_host
```

You can also specify the public key file and port, like this:

```bash
ssh-copy-id -i ~/.ssh/id_rsa.pub USER@HOST -p PORT
```

You may see the following message:

```bash
OutputThe authenticity of host '203.0.113.1 (203.0.113.1)' can't be established.
ECDSA key fingerprint is fd:fd:d4:f9:77:fe:73:84:e1:55:00:ad:d6:6d:22:fe.
Are you sure you want to continue connecting (yes/no)? yes
```

This means that your local computer does not recognize the remote host. This will happen the first time you connect to a new host. Type “yes” and press `ENTER` to continue.

Next, the utility will scan your local account for the `id_rsa.pub` key that we created earlier. When it finds the key, it will prompt you for the password of the remote user’s account:

```bash
Output/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
username@203.0.113.1's password:
```

Type in the password (your typing will not be displayed, for security purposes) and press `ENTER`.

The utility will connect to the account on the remote host using the password you provided.

It will then copy the contents of your `~/.ssh/id_rsa.pub` key into a file in the remote account’s home `~/.ssh` directory called `authorized_keys`.

You should see the following output:

```bash
OutputNumber of key(s) added: 1

Now try logging into the machine, with:   "ssh 'username@203.0.113.1'"
and check to make sure that only the key(s) you wanted were added.
```

#### Disable SSH Password Auth

Once you have pushed the SSH key to the remote host, you need to disable password authentication.

From a terminal session with the remote host, open the sshd\_config file (same as earlier), located at: /etc/ssh/sshd\_config.

Locate the line with 'PasswordAuthentication', and set it to no, like this:

```bash
...
PasswordAuthentication no
...
```

Save and close the config file.

Restart the ssh service, with this:

```bash
sudo systemctl restart ssh
```

Now, you have added the public SSH key of your local linux user account, to the remote Linux VM.  
And, you should have SSH key authenticated access to the remote Linux host.

# Adding SSH Keys with CAT

Here's a quick and dirty way to upload SSH keys to a remote user account, using the CAT command.

This can be done, if your local host doesn't have the ssh-copy-id utility.

If you do not have <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">ssh-copy-id</span> available, but you have password-based SSH access to an account on your server, you can upload your keys using a conventional SSH method.

We can do this by using the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">cat</span> command to read the contents of the public SSH key on our local computer and piping that through an SSH connection to the remote server.

On the other side, we can make sure that the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">~/.ssh</span> directory exists and has the correct permissions under the account we’re using.

We can then output the content we piped over into a file called <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">authorized\_keys</span> within this directory. We’ll use the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">&gt;&gt;</span> redirect symbol to append the content instead of overwriting it. This will let us add keys without destroying previously added keys.

The full command looks like this:

```bash
cat ~/.ssh/id_rsa.pub | ssh username@remote_host "mkdir -p ~/.ssh && touch ~/.ssh/authorized_keys && chmod -R go= ~/.ssh && cat >> ~/.ssh/authorized_keys"
```

You may see the following message:

```bash
OutputThe authenticity of host '203.0.113.1 (203.0.113.1)' can't be established.
ECDSA key fingerprint is fd:fd:d4:f9:77:fe:73:84:e1:55:00:ad:d6:6d:22:fe.
Are you sure you want to continue connecting (yes/no)? yes
```

This means that your local computer does not recognize the remote host. This will happen the first time you connect to a new host. Type `yes` and press `ENTER` to continue.

Afterwards, you should be prompted to enter the remote user account password:

```bash
Outputusername@203.0.113.1's password:
```

After entering your password, the content of your `id_rsa.pub` key will be copied to the end of the `authorized_keys` file of the remote user’s account.

You can then, attempt to use SSH key authentication.

# Linux SSH Key Management

See this article for an update based on obsolete SHA-1 RSA key usage: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://askubuntu.com/questions/1409105/ubuntu-22-04-ssh-the-rsa-key-isnt-working-since-upgrading-from-20-04" data-inline-card="true" data-renderer-start-pos="71"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Ubuntu 22.04 SSH the RSA key isn't working since upgrading from 20.04</span></span>](https://askubuntu.com/questions/1409105/ubuntu-22-04-ssh-the-rsa-key-isnt-working-since-upgrading-from-20-04)</span></span></span>

NOTE: We currently have two tutorials for this, that need to be consolidated into one. So, maybe this alternate tutorial fills your use-case: [How to Setup SSH Key Authentication to Linux](https://wiki.galaxydump.com/link/444 "https://wiki.galaxydump.com/link/444")

### Step 1 - Key Creation

Follow this page for creating keys in Linux: [Creating SSH Keys in Linux](https://wiki.galaxydump.com/link/210 "https://wiki.galaxydump.com/link/210")

Or, Follow this page for creating keys in Windows: [Creating SSH Keys in Windows](https://wiki.galaxydump.com/link/388 "https://wiki.galaxydump.com/link/388")

After following one of the above tutorials, you should have a public and private key that you can use to authenticate.

The next step is to place the public key on your server so that you can use SSH-key-based authentication to log in.

### Step 2 — Copying the Public Key to a Linux Server

There’s a couple ways to add an SSH public key to a remote host:

- Using SSH-Copy-ID
- Using CAT
- Manually Copying

#### Using SSH-Copy-ID

The quickest way to copy your public key to the Ubuntu host is to use a utility called `ssh-copy-id`. Due to its simplicity, this method is highly recommended if available.

See this page for how to use it: [Adding SSH Keys with ssh-copy-id](https://wiki.galaxydump.com/link/445 "https://wiki.galaxydump.com/link/445")

#### Using CAT

If you do not have `ssh-copy-id` available, but you have password-based SSH access to an account on your server, you can upload your keys using a conventional SSH method.

Here’s a method for uploading an SSH key with cat: [Adding SSH Keys with CAT](https://wiki.galaxydump.com/link/446 "https://wiki.galaxydump.com/link/446")

#### Manually Copying

If you don’t have SSH access to the remote host, you will need to directly paste in a user’s SSH public key.

See this page for how to manually add SSH keys to a Linux host: [Linux: Manually Installing SSH Keys](https://wiki.galaxydump.com/link/440 "https://wiki.galaxydump.com/link/440")

### Step 3 — Authenticating to Your Ubuntu Server Using SSH Keys

If you have successfully completed one of the procedures above, you should be able to log into the remote host *without* providing the remote account’s password.

See this page for how to: [Authenticating to Linux Server with SSH Keys](https://wiki.galaxydump.com/link/442 "https://wiki.galaxydump.com/link/442")

If key-based authentication was successful, continue on to learn how to further secure your system by disabling password authentication.

### Step 4 — Disabling Password Authentication on Your Server

Once you have confirmed that you can access the VM, with SSH key authentication, follow this page to disable password authentication: [Linux: Disabling Password Authentication](https://wiki.galaxydump.com/link/443 "https://wiki.galaxydump.com/link/443")

### Conclusion

You should now have SSH-key-based authentication configured on your server, allowing you to sign in without providing an account password.

If you’d like to learn more about working with SSH, take a look at our [SSH Essentials Guide](https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys "https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys").

# Linux VM Provisioning

Here are some steps that need to be done when creating a VM template that will be used multiple times:

## Template Usage

See this page to setup a deployed instance from a template VM: [Ubuntu Host Setup](https://wiki.galaxydump.com/link/245 "https://wiki.galaxydump.com/link/245")

## References

Here’s a good reference for template generation and cloud-init usage: [https://medium.com/@dsykes012/making-a-custom-ubuntu-20-04-lts-focal-fossa-vm-template-that-works-with-cloud-init-2cfffb6783b4](https://medium.com/@dsykes012/making-a-custom-ubuntu-20-04-lts-focal-fossa-vm-template-that-works-with-cloud-init-2cfffb6783b4 "https://medium.com/@dsykes012/making-a-custom-ubuntu-20-04-lts-focal-fossa-vm-template-that-works-with-cloud-init-2cfffb6783b4")

## Important Notes

Anytime you power on a Linux VM template, the VM thinks it is a spawned instance.  
And so, it will create new values, such as Machine-ID, SSH host keys, etc.  
This is the normal functionality of a spawned clone.

However. If you are simply powering up a template VM, to make changes or updates to the template, you will need to rerun the reset script, to clear out any set values (due to VM startup).  
To do this, follow the instructions at the bottom of this page, link here: [https://wiki.galaxydump.com/link/391#bkmrk-now.-each-time-you-s](https://wiki.galaxydump.com/link/391#bkmrk-now.-each-time-you-s)

<p class="callout warning">Warning: Forgetting to run the reset script, before shutting down the template VM, will result in clones having similar properties.</p>

## Host Template Setup

Here are steps for creating a golden template VM.

### Create Template VM

Create a VM guest instance with minimal CPU, memory, and hard disk space for the template.

We go with minimal resources, as it’s easier to script additional resources, than it is to reduce them.

For example, it’s much faster and less risk to expand a drive than reducing one.

Assign the VM’s NIC to the provisioning VLAN, as this allows us to easily access and setup deployed clones.

### DHCP Addressing

Setup the template with DHCP, so we don’t worry about address collisions when spawning multiple clones at once.

### Perform Updates and Upgrades

To speed up the process of clone setup, we will do any updates and upgrades on the template.  
Do this:

`apt update && apt -y upgrade && apt -y autoremove && apt clean`

### SSH Server

Install the openssh server:

```bash
 sudo apt install openssh-server
```

Verify it's running with this:

```bash
sudo systemctl status ssh
```

Configure it to listen on port 22 of all adapters.

Disable password authentication over SSH.

### Firewall

Setup the ufw firewall rules with 22 as allowed:

```bash
sudo ufw allow ssh
```

Don’t enable the firewall on the template. We can script this as use cases require.

Switching to Root

In order to perform most of these setup tasks, you should be running as root.

To switch to root (from an existing user), use this:

```
sudo -i
Or
sudo -
```

## Provisioning User Account

In order for us to manage and deploy to the host, it needs a user account.

Follow the instructions on this page, to create the user account and setup access to it: [Managed Host User Setup](https://wiki.galaxydump.com/link/395)

<span style="color: rgb(187, 187, 187); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 2.333em; font-weight: 400;">User Cleanup</span>

Log into the template VM as root.

This allows us to scrub unnecessary users and groups from the system.

### Reset Hostname

Every host needs a unique name in your network.

So, we will clear an default the hostname of a template.  
The following will erase it:

```bash
truncate -s0 /etc/hostname
```

And, this will set it to 'localhost':

```bash
hostnamectl set-hostname template01
```

<p class="callout info">NOTE: The above actions are performed by the reset script (bottom of this page).</p>

Next, we need to clear the hostname in the /etc/hosts file.

Open /etc/hosts, and set the entry (127.0.1.1) to 'template01', like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/lbNXrB3uEcCMPNaR-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/lbNXrB3uEcCMPNaR-image.png)

Save and close the hosts file.

<p class="callout warning">WARNING: This last step, to update the hosts file is NOT performed by the reset script (bottom of this page).  
You will need to perform the above step, manually, when creating the template.</p>

### Clear the Machine-Id

Machine-Id is a string value that uniquely identifies a linux host, similar to how a Windows VM is uniquely identified by its machine sid.

Linux stores the machine-id is stored in a file at: /etc/machine-id.

So. If you are cloning VMs, you need to make sure this value is different for each VM clone.  
The following will erase the machine-id, and any symbolic links to it:

```bash
truncate -s0 /etc/machine-id
rm /var/lib/dbus/machine-id
ln -s /etc/machine-id /var/lib/dbus/machine-id
```

The next time the VM reboots, the OS will generate a new machine-id during the boot process.

<p class="callout warning">NOTE: Each time you boot the template, creates a new machine Id.  
So, you will need to re-execute the above, after each time you boot the template VM for updates or changes.</p>

<p class="callout info">NOTE: The above actions are performed by the reset script (bottom of this page).</p>

### Reset Host SSH Keypairs

Every linux host holds a set of SSH keypairs that identify the host, and allow SSH connections with it.

For some hosts, there may be more than one keypair, each supporting a different encryption type: RSA, ECDSA, ed25519, etc...

These host SSH keys need to be unique for every host.  
But when cloning a VM, these keys get duplicated.  
So, we need a way for the linux template to reset its host keypairs. And for the clone to create a new set when it first boots.

The easiest way, is for us to delete the SSH keys from the template.  
And, to create a one-shot service that will create a new set on startup.

On the template, we need to delete all host SSH keys that it possesses, with this:

```bash
sudo rm -f /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub
```

<p class="callout info">NOTE: The above action is performed by the reset script (bottom of this page).</p>

For a clone of the template to recreate host SSH keys, we need to create a new systemd daemon, that will create the new keys on startup, only if they don't yet exist.

We do so, by creating a systemd unit file, with this:

```bash
sudo nano /etc/systemd/system/ssh-hostkeys-generate.service
```

And, we populate the service unit file with this:

```ini
[Unit]
Description=Generate SSH host keys if missing
DefaultDependencies=no
After=local-fs.target
Before=sshd.service ssh.service
ConditionPathExistsGlob=!/etc/ssh/ssh_host_*_key

[Service]
Type=oneshot
ExecStart=/usr/bin/ssh-keygen -A
# If you only want ed25519 + ecdsa, comment the line above and use:
# ExecStart=/bin/sh -c '/usr/bin/ssh-keygen -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key; \
#                       /usr/bin/ssh-keygen -t ecdsa   -b 256 -N "" -f /etc/ssh/ssh_host_ecdsa_key'

[Install]
WantedBy=multi-user.target

```

The above service definition includes a check if any SSH keypairs exist in Line 6: `ConditionPathExistsGlob=!/etc/ssh/ssh_host_*_key`

This prevents the service from running more than once.

Once the systemd file is created, we need to reload the daemon and enable the service, with these:

```
sudo systemctl daemon-reload
sudo systemctl enable ssh-hostkeys-generate.service
```

### Template Reset Script

Here's the definition of the template reset script that is used to clean up a linux template VM, for cloning.

The reset script will:

- Reset the machine-id (same as described above)
- Clear the host SSH keypairs
- Clear the hostid
- Clear the hostname
- Clear the hostname in /etc/hosts
- Clear existing DHCP leases
- Reset the system entropy seed
- Clear logs and history
- Clear user traces
- Clear package caches
- Clear any Docker clientid
- Remove any Docker cached layers

The script should be stored, here: /etc/template/reset.sh

To create it, first, create the template folder, with this:

```bash
sudo mkdir /etc/template
```

Once the folder exists, you can create the script file, with this:

```bash
sudo nano /etc/template/reset.sh
```

And, populate it with this code:

```bash
#!/bin/bash

# Name:          Ubuntu 24.04 Template Reset Script.
# Version:       1.0
# Date:          20250816
# See this page: https://wiki.galaxydump.com/link/391
# Description:   This script Will clear hostnames, leases, ids, keys, and other properties,
#                    before shutting down the template.
#                This allows a clone to be made, and started up, with its own unique Ids, keys, etc.

set -euo pipefail

echo "Clearing host SSH keys..."
rm -f /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub

echo "Clearing host machine-id..."
truncate -s0 /etc/machine-id
rm /var/lib/dbus/machine-id
ln -s /etc/machine-id /var/lib/dbus/machine-id

echo "Clearing host machine-id..."
# systemd will recreate on boot; or: dd if=/dev/urandom of=/etc/hostid bs=4 count=1
# 20250816 Update: We've disabled the removal of the hostid file, on Ubuntu24, as this distro does
#     NOT recreate the file on startup.
# As well, the hostid file is only used by some legacy apps/libraries.
# So, if a truly unique hostid is required, we will have to create a one-shot systemd service that
#     will generate it on first-boot of a clone.
#rm -f /etc/hostid || true

echo "Clearing hostname..."
truncate -s0 /etc/hostname
hostnamectl set-hostname localhost

echo "Clear Existing DHCP Leases..."
# Debian/Ubuntu dhclient
rm -f /var/lib/dhcp/* || true
# NetworkManager leases
rm -f /var/lib/NetworkManager/*lease* || true
# systemd-networkd leases
rm -f /var/lib/systemd/network/* || true

echo "Let systemd reseed entropy on first-boot..."
rm -f /var/lib/systemd/random-seed || true

echo "Clear logs and history..."
journalctl --rotate || true
journalctl --vacuum-time=1s || true
rm -f /var/log/wtmp /var/log/btmp || true
: | tee /var/log/lastlog >/dev/null
find /var/log -type f -name '*.log' -exec truncate -s0 {} +

echo "Clear User Traces..."
rm -f /root/.bash_history || true
find /home -maxdepth 2 -name .bash_history -exec rm -f {} + || true

echo "Clear Package Caches..."
# Debian/Ubuntu
apt-get clean || true;
# RHEL/CentOS/Alma/Rocky
#dnf clean all || yum clean all

echo "Clear any Docker ClientId..."
#Container runtime: if templating with Docker installed:
# Remove the client ID file...
rm -f /etc/docker/key.json
# Remove cached layers.
rm -f /var/lib/docker

echo "Template sealed. Power off, convert to template, and ensure first-boot key regen is enabled."

```

Save and close the script file.

Now, make sure it's executable, with this:

```bash
sudo chmod +x /etc/template/reset.sh
```

Now. Each time you shutdown the template VM, you can run the above script, to reset needed info, for cloning.

The script can be executed, with this:

```bash
sudo /etc/template/reset.sh
```

# Linux: Allow User to Skip Sudo Challenge

When scripting command line work, you will come across the need to respond to sudo challenges, to execute commands that require elevation, if not running as root.

You can choose to pass the user's password as stdinput, using the command line gymnastics at the bottom of this: [C# Dealing with Sudo](https://wiki.galaxydump.com/link/89)

Or, you can allow a specific user, members of a group, or all users (if you really hate sudo), to skip sudo challenges, by adding them to the sudoers group, with a NOPASSWD flag.

If you are looking to remove complexity from your automated scripting, or just really annoyed with having to type your password everytime you use a privileged command, this page will show you how to bypass sudo checks.

You can bypass sudo checks for a single user, a group, or for all.

You can specify what apps can be called without sudo.  
Or, make it wide-open.

The intended use case for this page is how to setup a user account that will be used by automated scripting for provisioning and deployment.

<p class="callout info">NOTE: Changes to visudo are immediate, since the file is read each time sudo is called.  
So. You don't have to logout, restart a service, or reboot for changes to take effect.</p>

#### How To

Open the sudoers file with this:

```bash
sudo visudo -f /etc/sudoers
```

Now, you can add your entries, above the @includedir line.

#### For a Single User

<p class="callout info">NOTE: This level of sudo bypass is a reasonable choice for automated scripting.</p>

If you want to grant passwordless sudo for a single user, add a line, like this:

```bash
username ALL=(ALL) NOPASSWD: ALL
```

- `username`: the actual username
- `ALL`: means from any host (relevant in multi-user systems)
- `(ALL)`: means the user can run commands as any user
- `NOPASSWD: ALL`: no password will be required for any `sudo` command

#### For Members of a Group

If you want to grant passwordless sudo for a single user, add a line, like this:

```bash
%groupname ALL=(ALL) NOPASSWD: ALL
```

- `groupname`: the name of the group
- `ALL`: means from any host (relevant in multi-user systems)
- `(ALL)`: means the user can run commands as any user
- `NOPASSWD: ALL`: no password will be required for any `sudo` command

#### Limit App Usage

When declaring a sudo bypass entry, you can constrain what apps or commands can be called without sudo, like this:

```bash
deploy ALL=(ALL) NOPASSWD: /usr/bin/apt-get, /bin/systemctl restart *, /usr/bin/useradd
```

# Linux: Missing .SSH Folder

When creating a new linux user, the system may not create a .ssh folder in the user profile, by default.

This creates a little complexity as the permissions are a bit picky.

Here are steps to do so.

<p class="callout info">NOTE: Replace 'username' with the target user.</p>

Create the .ssh folder in the user's profile:

```bash
sudo mkdir -p /home/username/.ssh
```

Set correct permissions for the .ssh folder:

```bash
sudo chmod 700 /home/username/.ssh
```

Create the authorized\_keys file:

```bash
sudo touch /home/username/.ssh/authorized_keys
```

Set permissions on authorized\_keys file:

```bash
sudo chmod 600 /home/username/.ssh/authorized_keys
```

Set ownership of the .ssh folder to the user:

```bash
sudo chown -R username:username /home/username/.ssh
```

Now, you can add keys to the authorized\_keys file as needed, for remote SSH authentication.

#### Scripted Creation

The above commands can be quickly performed with this bash script:

```
#!/bin/bash
USERNAME="deploy"

sudo mkdir -p /home/$USERNAME/.ssh
sudo touch /home/$USERNAME/.ssh/authorized_keys
sudo chmod 700 /home/$USERNAME/.ssh
sudo chmod 600 /home/$USERNAME/.ssh/authorized_keys
sudo chown -R $USERNAME:$USERNAME /home/$USERNAME/.ssh
```

# Linux: Shell Appearance

If you ever log into a linux host, and the command prompt has only a '$', and there is no scrollable command history, then the shell may not be set to bash.

#### Shell Assignment

You can check what shell is assigned to the user with this:

```bash
getent passwd username
```

You will see something like this:

```

username:x:1001:1001::/home/username:/bin/bash
```

If the shell is something like `/usr/sbin/nologin`, `/bin/sh`, or blank, change it to bash:

```bash
sudo chsh -s /bin/bash username
```

Then, log out and back in, and you will have a bash shell.

#### Not Launching Interactively

SSH by default starts a **non-login shell**, and some files like `.bashrc` or `.bash_profile` may be missing.

To correct this, ensure the following files exist in the user's home directory:

```bash
# Copy default skeleton files to get a usable shell config
sudo cp /etc/skel/.bashrc /home/username/
sudo cp /etc/skel/.profile /home/username/
sudo chown username:username /home/username/.bashrc /home/username/.profile
```

#### Home Directory Permissions

If that doesn't fix it, the user's home directory may have weird permissions.

To fix that, run these:

```
sudo chown -R username:username /home/username
sudo chmod 755 /home/username
```

#### Bash History

Bash normally writes to ~/.bash\_history.

If Bash history is not being saved on exit, you can fix it with this:

```bash
sudo touch /home/username/.bash_history
sudo chown username:username /home/username/.bash_history
chmod 600 /home/username/.bash_history
```

Also. Make sure the history file is not disabled, with this:

```bash
echo $HISTFILE
# Should output something like /home/username/.bash_history
```

# Managed Host User Setup

For hosts that will be managed by Ansible, or some other automated method, the host will need a user account with proper access.

Follow these steps to setup the account and access.

<p class="callout info">NOTE: This page assumes that SSH server is setup.  
And, that the firewall rules are setup for remote SSH access.</p>

## Provisioning User Account

#### Create User

Create a provisioning user on the host.

<p class="callout info">NOTE: If not running as root, you will need to prepend these commands with 'sudo'.</p>


<p class="callout info">NOTE: By default, we use the username 'provisioner'.</p>

Run this to create the user with a password:

```bash
useradd -m provisioner
```

<p class="callout info">NOTE: We included the '-m' switch, to give the user a home directory.  
This is done, so that our user can authenticate with SSH keys, which need a .ssh folder under the user's home directory.</p>

Once created, add the provisioner account to the sudo group, so it can run elevated commands:

```bash
usermod -aG sudo provisioner
```

<p class="callout info">NOTE: The `-aG` option tells `usermod` to add the user to the listed groups.</p>

#### User Shell

If you want the provisioner user to have shell access, set the shell with this:

```bash
sudo chsh -s /bin/bash provisioner
```

See this page for details: [Linux: Shell Appearance](https://wiki.galaxydump.com/link/394)

<p class="callout info">NOTE: It may not be necessary for an automation account to have a shell.  
But, it does help when troubleshooting.</p>

#### Account Password

You need to choose if you want the provisioner account to have a password or not.

<p class="callout info">NOTE: You don't really have to create a password for a provisioning user account if:  
 - You will use SSH keys for authentication,  
 - You will be bypassing sudo checks for this user</p>

If you want it to have a password, use this to create one:

```bash
passwd provisioner
```

If you don't want the user to have a password, run this:

```
passwd -l provisioner
```

The above will lock the password, making login via password impossible.

#### Sudo Bypass

We will allow the provisioner user to skip any sudo challenge checks.  
We do this, so that we don't have to manage passwords in the deployment scripting.

Follow this page for how to do that: [Linux: Allow User to Skip Sudo Challenge](https://wiki.galaxydump.com/link/392)

Edit the visudo file with this:

```bash
visudo -f /etc/sudoers
```

And, add this line above the includedir line:

```bash
provisioner ALL=(ALL) NOPASSWD: ALL
```

#### SSH Key Installation

Install an SSH key for the user.

<p class="callout info">NOTE: If you are just setting up this user, it may not yet have an authorized\_keys file, or an .ssh folder in its profile.</p>

If the .ssh folder is missing, follow this: [Linux: Missing .SSH Folder](https://wiki.galaxydump.com/link/393)

To do so, locate the authorized\_keys file for the provisioner user at: /home/provisioner/.ssh/authorized\_keys.  
And, add the public key of the provisioner account.

Open the authorized\_keys file for the provisioner's account, with this:

```bash
sudo nano /home/provisioner/.ssh/authorized_keys
```

Paste in the public key string of the provisioner account's SSH public key.

The current provisioner account SSH public key is found here:

- On Build Server:  
    "/mnt/secshare/oga/keys/provisioner\_user/ecdsa-key-provisioner-buildserver01-20250803.pub"
- In Secure Share:  
    "\\SecureShare git\\oga\\keys\\provisioner\_user"

Save and close when done.

#### Verify SSH Access

Once the SSH key is loaded, you can verify SSH authentication with this:

```bash
ssh -i /path/to/privatekeyfile provisioner@hostname
```

To test access with the provisioner's current SSH key file, use something like this:

```
ssh -i /mnt/secshare/oga/keys/provisioner_user/ecdsa-key-provisioner-buildserver01-20250803.key provisioner@hostname
```

### Update Ansible Hosts File

For Ansible to reach the host, you need to give Ansible some information about it.

#### Live Ansible Hosts File

This is stored in the Ansible hosts file.

The live hosts file is on the Ansible server at: /etc/ansible/hosts

Add the host to it, like this:

```bash
[Name of host group]
hostname ansible_host=192.168.120.99 ansible_user=provisioner ansible_ssh_private_key_file=/mnt/secshare/oga/keys/provisioner_user/ecdsa-key-provisioner-buildserver01-20250803.key
```

The above is a host entry in the Ansible hosts file.

It has the following parts, in this order:

- Hostname - This is name used by ansible plays to identify the host.
- ansible\_user - This is the username that Ansible will login with. Set it to the account name, from above.
- ansible\_ssh\_private\_key\_file - This is the path to the current SSH private key used for deployment.

The current provisioning private key file, for Ansible, is in the mapped secure share, here:

```
/mnt/secshare/oga/keys/provisioner_user/ecdsa-key-provisioner-buildserver01-20250803.key
```

#### Org Hosts File

As well, there's a hosts file in each Org's Infrastructure project, as well.

This reflects what ansible host entries are relevant to that org.

Copy the same host entry into that file, to keep it in sync.

# HowTo Configure IIS Reverse Proxy

<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://medium.com/@gusterwoei/how-to-configure-reverse-proxy-on-windows-iis-52a48b90163a" data-inline-card="true" data-renderer-start-pos="1"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">How to configure reverse proxy on Windows IIS</span></span>](https://medium.com/@gusterwoei/how-to-configure-reverse-proxy-on-windows-iis-52a48b90163a)</span></span></span>

# How to Add Root CAs to Ubuntu

Reference: [https://ubuntu.com/server/docs/install-a-root-ca-certificate-in-the-trust-store](https://ubuntu.com/server/docs/install-a-root-ca-certificate-in-the-trust-store)

Install the CA certificate package:

```bash
sudo apt-get install -y ca-certificates
```

Now, you can install any local intranet root CA, with this:

<p class="callout info">NOTE: We are copying in the .crt file.  
Once the trust bundle is created, with a later step, the file will be converted to PEM format.</p>

```
sudo cp ogsofttech.lan_ca.crt /usr/local/share/ca-certificates
```

Once you have all the certificates loaded, you need to add them to the trust store, with this:

```bash
sudo update-ca-certificates
```

Once done, you can verify that your certificate is in pem format:

```bash
sudo ls /etc/ssl/certs/ | grep ogsofttech.lan_ca
```

You will see something like this, if loaded into the trust bundle:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/oon0eR91hMZejMKT-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/oon0eR91hMZejMKT-image.png)

If you are looking for the local intranet Root CA certificate, see this: [Current Intranet Root CA Certificate](https://wiki.galaxydump.com/link/454#bkmrk-current-intranet-roo)

# Ubuntu Server Setup



# Ubuntu Host Setup

Here are the minimal steps to setup a clean Ubuntu VM.

<p class="callout info">Note: These instructions are tested on on Ubuntu v22 through v24.  
They may require updates for other versions.</p>

#### Spawn VM Clone

The first step is to spawn a clone of the template VM.

Be sure to do the following:

1. Give it an inventory name that fits its hostname.
2. Update the CPU count.
3. Set the memory size.
4. Set its disk space for the intended service.
5. Assign the VM's NIC to the Provisioning portgroup (VLAN 170).  
    This will ensure that we can access it via SSH, for faster setup.

Once the VM is started, log into its console session.

#### Update Packages

From the console, update packages of the VM.  
Do this before anything else, to ensure the latest package versions are used.

```bash
sudo apt-get update && sudo apt-get upgrade -y
```

#### Ubuntu: SSH Server

See this page for setting up the SSH Server: [Ubuntu: SSH Server](https://wiki.galaxydump.com/link/118)

#### Initial Remote Access (VLAN 170)

Once the VM is started up, and in the Provisioning VLAN (VLAN 170), it should have a DHCP address that we can reach.

From the VM's local console, run this to get its IP on the provisioning VLAN:

```bash
ifconfig
```

Open an SSH session to the VM, for remote setup.

#### Setting the Host Name

See this page for how to set the hostname: [Ubuntu: Set Hostname](https://wiki.galaxydump.com/link/439)


#### Other Packages

We will install net-tools on each host, for diagnostic purposes.

```bash
sudo apt install net-tools
```

#### Managed Host Setup

If the VM will be managed by Ansible and deployment tools, see this page for setup steps: [Managed Host User Setup](https://wiki.galaxydump.com/link/395)

#### User Setup

Configure any users and groups that the VM will need.

This may include a deployment user.

#### SSH Keys

We need to add SSH public keys for users added, above.

See this page for the various method of adding SSH keys to a remote linux host: [Linux SSH Key Management](https://wiki.galaxydump.com/link/447)

#### Switch SSH to Key Auth

Once you have installed SSH public keys in the VM, you need update the SSH config to SSH authentication.

To do so, open the sshd config file, with this:

```bash
sudo nano /etc/ssh/sshd_config
```

Look for the line with this directive, 'PasswordAuthentication'.

Uncomment the line, and set it to no, like this:

```bash
PasswordAuthentication no
```

In the config file, set Key Auth to yes:

```bash
PubkeyAuthentication yes
```

Save and close the SSH config file.

Restart SSH with this:

```bash
sudo systemctl restart ssh
```

#### Verify SSH Keys

Now that SSH key authentication is enabled, you need to verify that each added SSH public key works.

Attempt to connect with the VM, using each configured SSH key.  
Verify each one works.

See this page for steps on how to connect to a Linux host from Windows: [Connecting to SSH Server from Windows](https://wiki.galaxydump.com/link/441)

#### Routing and Firewall

So far, we are accessing the VM on a temporary provisioning network.  
We need to set things up for its final location.

Create a proper firewall rule for accessing the VM at its final IP address, in its target VLAN.



#### Static IP Address

With the above firewall rule in place, we will be able to access the VM, once it's moved to its target VLAN.

But first, we need to set its static IP address: [Ubuntu: Setup Static IP Address](https://wiki.galaxydump.com/link/119)

#### Change VLAN PortGroup

Setting the static IP address, above, means that we lost temporary SSH access.  
We need to fix that.

In the hypervisor, change the VM's portgroup to the target VLAN.

#### Remote Access

With the VM at its assigned static IP, in the target VLAN, and with access firewall rule exists, we can remote SSH to it.

Attempt to open a remote SSH session to the VM.

#### Root CA Certs

If you are looking for the local intranet Root CA certificate, see this: [Current Intranet Root CA Certificate](https://wiki.galaxydump.com/link/454#bkmrk-current-intranet-roo)

If your institution or company uses has its own certificate authority (CA), you should install its root CA into the following folder:

```
/usr/local/share/ca-certificates/
```

For example, to add the root CA for the local network, create the file with this:

```bash
sudo nano /usr/local/share/ca-certificates/ogsofttech.lan_ca.crt
```

Save and close the file.

Once all root CA certs are added, you need to update the runtime's CA list, with this:

```bash
sudo update-ca-certificates
```

See this page for how to add Root CA certificates: [How to Add Root CAs to Ubuntu](https://wiki.galaxydump.com/link/432)

#### NTP Client Needs

If the VM will be located in an isolated VLAN with restricted internet access, it may need access to the local NTP server.

See this page for how to setup each host/VM to use the local private NTP server: [Ubuntu: Use Private NTP Server](https://wiki.galaxydump.com/link/457)


#### Further Setup

With the above things done, we can continue on with other setup.

# Ubuntu: Setup SSH Server

Here are steps to setup the SSH server on an Ubuntu host.

Here's a good reference article for how to do what we're doing here: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://linuxize.com/post/how-to-enable-ssh-on-ubuntu-20-04/" data-inline-card="true" data-renderer-start-pos="98"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="css-1cwva94 e1o1fuf52" data-testid="inline-card-icon-and-title"><span class="smart-link-title-wrapper css-0 e1o1fuf57">How to Enable SSH on Ubuntu 20.04</span></span>](https://linuxize.com/post/how-to-enable-ssh-on-ubuntu-20-04/)</span></span></span>

Here are the commands to execute:

```bash
sudo apt update
```

Install the SSH server with this:

```bash
sudo apt install openssh-server
```

Start the SSH server:

```bash
sudo systemctl enable --now ssh
```

Verify it is running:

```bash
sudo systemctl status ssh
```

Add a firewall rule for the SSH server:

```bash
sudo ufw allow ssh
```

# Ubuntu: Setup Static IP Address

Here are notes and steps to assign a static IP address for an Ubuntu host.

These steps are known to work with Ubuntu 22 and 24.

<p class="callout info">NOTE: If you are doing for a Debian host, see this page: [Debian: Setup Static IP Address](https://wiki.galaxydump.com/link/577)</p>

#### Net Tools (ipconfig)<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Install net tools, so we can use commands like: ipconfig

`sudo apt install net-tools`

#### Static IP Address<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Static addresses will be defined in a netplan file, configured below.

But, we need to do a few things, first:

- Get the Gateway IP
- Enable Adapters

#### Get the Gateway IP

You will need to know the gateway address that your host will use.

If the host is not on the desired network, you will need to determine the gateway IP, manually.  
Or, you can join the host to the network, and do the following to determine it.

If the host is up, you can run this command to get the current default gateway assigned to it:

`ip r | grep default`

This command will return the default gateway address, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/scaled-1680-/h6MJkA6IxvRSQ4ii-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/h6MJkA6IxvRSQ4ii-image.png)

#### Enable Adapters<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

If you added an adapter to the host, it may be in a down state.

To enable it, use this command to find the name:

```bash
sudo ip a | grep ^[[:digit:]]
```

The above will give a list, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/scaled-1680-/uVSzS5rVVjzGu6Oi-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/uVSzS5rVVjzGu6Oi-image.png)

And, you can enable it with this:

```bash
sudo ifconfig eth1 up
```

<p class="callout info">NOTE: Be sure to use the name of the nic, from the previous call.</p>

#### NetPlan YML<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

We will assign our static address, via NetPlan.

Open the netplan file, so you can edit the network config.  
It will be located here:

`/etc/netplan/`

### Existing Cloud YML<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

If this folder contains a cloud yaml, you may have to disable cloud network config.  
To do so, create a file at:

`/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg`

And, give it this content:

`network: {config: disabled}`

Delete the existing cloud yml file, so it does not interfere with the config you will build, next.

Now, you can write a config yaml file that will be accepted through a reboot.

### Editing NetPlan YML<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

If a non-cloud netplan yml file exists, you may be able to edit it. The file on your host may have a different name, depending on OS version.  
Check the filename before running this command.

Here’s the name of a valid netplan file that can be created, if one does not already exist.

`sudo nano /etc/netplan/01-network-manager-all.yaml`

Edit the file to reflect the following.  
Be sure to use your ethernet adapter name (enps3s0), or whatever its name was discovered to be earlier.

Create the netplan file with the above name, `01-network-manager-all.yaml`

Set it up with your ether adapter names, static address, DNS, and gateway addresses.

The following are examples you can use:

```yaml
network:
    version: 2
    renderer: networkd
    ethernets:
        enp3s0:
            addresses:
                - 10.10.10.2/24
            nameservers:
                search: [mydomain, otherdomain]
                addresses: [10.10.10.1, 1.1.1.1]
            routes:
                - to: default
                  via: 10.10.10.1
```

Here’s a working netplan file, in use on AWS, that has two NICs (public and private subnets):

```yaml
# This sample has two network adapters, each in different subnets.
# The first NIC has a configured gateway and DNS.
# The second NIC is in a private subnet.
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      addresses:
        - 10.0.1.10/24
      nameservers:
        addresses: [127.0.0.53,8.8.8.8,8.8.4.4]
      routes:
        - to: default
          via: 10.0.1.1
    eth1:
      addresses:
        - 10.0.2.10/20
```

Create the Save changes to the file.

Make sure the created netplan file is only accessible by root, by doing this:

```bash
sudo chmod 600 ./01-network-manager-all.yaml
```

And, apply changes with:

```bash
sudo netplan apply
```

Check that connectivity exists through a reboot, to make sure settings persist.

# Ubuntu: Set Hostname

Here are steps to properly set the hostname of an Ubuntu server.

#### HostnameCtl

Get the current hostname with this:

```bash
hostnamectl
```

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-05/scaled-1680-/Kl3ruPr1mCRlCusB-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-05/Kl3ruPr1mCRlCusB-image.png)

To set it, use this:

```bash
sudo hostnamectl set-hostname new-hostname (replace new-hostname)
```

Once set, confirm it with the previous command.

#### Hosts File

Now, you need to update the hostname in your hosts file, with this:

```
sudo nano /etc/hosts
```

The hosts file lists the hostname in the second entry: 127.0.1.1, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/ZU6887t9afTNg3ft-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/ZU6887t9afTNg3ft-image.png)

Update the 127.0.1.1 entry with the correct hostname.

Save and close the hosts file after update.

# Linux: Manually Installing SSH Keys

Here's some steps on how to manually install SSH public keys in a host.

#### 1. SSH Key Folder<button aria-hidden="true" class="cc-vhh4ux" data-testid="anchor-button" type="button"><svg class="_1reo15vq _18m915vq _syaz1r31 _lcxvglyw _s7n4yfq0 _vc881r31 _1bsbpxbi _4t3ipxbi" fill="none" role="presentation" viewbox="0 0 16 16"></svg></button>

Navigate to the home folder for the user, with the following command:

```bash
cd /home/username
```

Check if the .ssh folder exists (it is hidden, requiring the -a switch):

```bash
ls -al
```

If the .ssh folder does not exist, create it with the following commands (from the user folder):

```bash
sudo mkdir ~/.ssh
```

```bash
sudo chmod 0700  ./.ssh
```

#### 2. SSH Key File

Enter the ssh key folder with:

```bash
cd ./.ssh
```

And, check if any key files are there, with:

```bash
ls -l
```

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/3Mcn7KKxyMbGIyTR-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/3Mcn7KKxyMbGIyTR-image.png)

Make sure the key file has the same name that was defined in the ssh config file, in previous steps.

If no key file, create one with (making sure to use the correct key file name):

```bash
sudo touch ./authorized_keys
```

Set permissions on the key file:

```bash
sudo chmod 600 ./authorized_keys
```

#### From Windows

This section is for copying the public key string from a Windows, host.

The tricks to successfully pasting in an SSH key to the ssh key file are:

- Always paste the key string as a single line
- The key string must being with, “ssh-rsa“
- Strip out any “Begin SSH2 PUBLIC KEY” and ending
- The key line should contain the key comment at the end of the line, for easy identification
- A key string should be of the form: ssh-rsa \[really long base64 key string here\] \[key comment\]
- Single whitespace is needed between each component of the key line
- The key comment must have no whitespace in it

The easiest way to get this string is to load a key in PuttyGen.  
Then, paste the entire key string directly from the text window of the form, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/QzY2Ss7qNnaMRONl-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/QzY2Ss7qNnaMRONl-image.png)

#### From Linux

If you do not have password-based SSH access to your server available, you will have to complete the above process manually.

We will manually append the content of your <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">id\_rsa.pub</span> file to the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">~/.ssh/authorized\_keys</span> file on your remote machine.

To display the content of your <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">id\_rsa.pub</span> key, type this into your local computer:

```bash
cat ~/.ssh/id_rsa.pub
```

You will see the key’s content, which should look something like this:

```
Outputssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqql6MzstZYh1TmWWv11q5O3pISj2ZFl9HgH1JLknLLx44+tXfJ7mIrKNxOOwxIxvcBF8PXSYvobFYEZjGIVCEAjrUzLiIxbyCoxVyle7Q+bqgZ8SeeM8wzytsY+dVGcBxF6N4JS+zVk5eMcV385gG3Y6ON3EG112n6d+SMXY0OEBIcO6x+PnUSGHrSgpBgX7Ks1r7xqFa7heJLLt2wWwkARptX7udSq05paBhcpB0pHtA1Rfz3K2B+ZVIpSDfki9UVKzT8JUmwW6NNzSgxUfQHGwnW7kj4jp4AT0VZk3ADw497M2G/12N0PPB5CnhHf7ovgy6nL1ikrygTKRFmNZISvAcywB9GVqNAVE+ZHDSCuURNsAInVzgYo9xgJDW8wUw2o8U77+xiFxgI5QSZX3Iq7YLMgeksaO4rBJEa54k8m5wEiEE1nUhLuJ0X/vh2xPff6SQ1BL/zkOhvJCACK6Vb15mDOeCSq54Cr7kvS46itMosi/uS66+PujOO+xt/2FWYepz6ZlN70bRly57Q06J+ZJoc9FfBCbCyYH7U/ASsmY095ywPsBo1XQ9PqhnN1/YOorJ068foQDNVpm146mUpILVxmq41Cj55YKHEazXGsdBIbXWhcrRf4G2fJLRcGUr9q8/lERo9oxRm5JFX6TCmj6kmiFqv+Ow9gI0x8GvaQ== demo@test

```

Access your remote host using whichever method you have available.

Once you have access to your account on the remote server, you should make sure the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">~/.ssh</span> directory exists. This command will create the directory if necessary, or do nothing if it already exists:

```bash
mkdir -p ~/.ssh
```

Now, you can create or modify the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">authorized\_keys</span> file within this directory. You can add the contents of your <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">id\_rsa.pub</span> file to the end of the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">authorized\_keys</span> file, creating it if necessary, using this command:

```bash
echo public_key_string >> ~/.ssh/authorized_keys
```

In the above command, substitute the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">public\_key\_string</span> with the output from the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">cat ~/.ssh/id\_rsa.pub</span> command that you executed on your local system. It should start with <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">ssh-rsa AAAA...</span>.

Finally, we’ll ensure that the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">~/.ssh</span> directory and <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">authorized\_keys</span> file have the appropriate permissions set:

```
chmod -R go= ~/.ssh
```

This recursively removes all “group” and “other” permissions for the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">~/.ssh/</span> directory.

If you’re using the **root** account to set up keys for a user account, it’s also important that the <span class="code" data-prosemirror-content-type="mark" data-prosemirror-mark-name="code" spellcheck="false">~/.ssh</span> directory belongs to the user and not to **root**:

```bash
chown -R sammy:sammy ~/.ssh
```

<p class="callout info">NOTE: The above example uses sammy as the username. Change this to the appropriate username for the target account.</p>

### Connecting to SSH Server from Windows

See this page for steps on how to connect to a Linux host from Windows: [Connecting to SSH Server from Windows](https://wiki.galaxydump.com/link/441)

# How to Setup SSH Key Authentication to Linux

This how to consolidates several aspects of setting up SSH key access to a Linux server.

<p class="callout info">NOTE: We currently have two tutorials for this, that need to be consolidated into one. So, maybe this alternate tutorial fills your use-case: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://oga.atlassian.net/wiki/spaces/~311198967/pages/47415311" data-inline-card="true" data-renderer-start-pos="233"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Ubuntu SSH Key Access</span></span>](https://oga.atlassian.net/wiki/spaces/~311198967/pages/47415311)</span></span></span></p>

<p class="callout info">NOTE: If you are creating ssh keys for recent Ubuntu distributions, do not use RSA, as it is no longer an accepted key type (as of Ubuntu 21, I think). Use ecdsa instead.</p>

## SSH Server Setup

Follow this link to setup the SSH server on Ubuntu: [Ubuntu: Setup SSH Server](https://wiki.galaxydump.com/link/118 "https://wiki.galaxydump.com/link/118")

### Update SSH Config

The SSH service needs to be configured to disallow passwords, and to require SSH key authentication.  
The following is list of actions to perform in the service configuration.

#### SSH Key File Name

<p class="callout info">NOTE: Not all ssh configuration files include the naming of the authorizedkeys file for users.  
And, different flavors of Ubuntu and linux use different naming conventions for the SSH key file of a user.</p>


So, we have to determine the file name for ssh key files, and make sure it is set in the config file, and we use the same name.

To find the ssh key file name, open the ssh config with the following:

```bash
sudo nano /etc/ssh/sshd_config
```

Locate the line in the config file with, AuthorizedKeysFile. Uncomment the line if necessary.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/JOEgve5BxdipkcU0-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/JOEgve5BxdipkcU0-image.png)

Make a note of the filename used. This will be needed when installing SSH keys.

#### Prevent Remote Root Login

This is also done in the ssh config file.

Locate the line with PermitRootLogin, and set it to no, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/0CIw38glMCsc4g33-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/0CIw38glMCsc4g33-image.png)

#### Disable Password Authentication

This requires three changes in the ssh config file.

Locate the line with ChallengeResponseAuthentication, and set it to no, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/yP7yJFVxFbQc5kAl-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/yP7yJFVxFbQc5kAl-image.png)

Locate the UsePAM line and set it to no, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/TBpK1oZHxoPzH0Bw-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/TBpK1oZHxoPzH0Bw-image.png)

Locate the PasswordAuthentication line and set it to no, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/UxAAFdidAnYQvaaT-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/UxAAFdidAnYQvaaT-image.png)

Disable interactive keyboard authentication by setting this:

```
KbdInteractiveAuthentication no
```

Save changes to the config file.

#### Restart SSH

With the changes made above, we need to restart the SSH service, with this:

```bash
sudo systemctl restart ssh
```

# SSRS



# SSRS: Export Reports Without WebPortal

If you’re trying to export all the RDL, data sources, gif, and other files of a set of reports, but the SSRS web portal is inaccessible, this method will pull them from the report engine database back-end.

Taken from here: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://gist.github.com/jzabroski/346ab6bd2dd0988fe5b6e1f660023470" data-inline-card="true" data-renderer-start-pos="225"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Export of SSRS reports datasources and images</span></span>](https://gist.github.com/jzabroski/346ab6bd2dd0988fe5b6e1f660023470)</span></span></span>

```powershell
<# .SYNOPSIS
        Export of SSRS reports datasources and images
    .DESCRIPTION
        This PowerShell script exports all (or filtered) reports, data sources and images directly from the ReportServer database
        to a specified folder. For the file name the complete report path is used; for file name invalid characters are replaced with a -.
        Reports are exported with .rdl as extension, data sources with .rds and resources without any additional extension.
        Please change the "Configuration data" below to your enviroment.
        Works with SQL Server 2005 and higher versions in all editions.
        Requires SELECT permission on the ReportServer database.
    .NOTES
        Author  : Olaf Helper
        Requires: PowerShell Version 1.0, Ado.Net assembly
    .LINK
        GetSqlBinary: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.getsqlbinary.aspx
#>
param(
        [Parameter(Mandatory=$true)] [string]$serverName, # server name and instance
        [Parameter(Mandatory=$true)] [string]$databaseName, # ReportServer Database.
	    [Parameter()] [string]$outputDirectory = 'C:\Temp\' # Path to export the reports to.
)

# Select-Statement for file name & blob data with filter.
$sql = "SELECT CT.[Path]
                ,CT.[Type]
                ,CONVERT(varbinary(max), CT.[Content]) AS BinaryContent
        FROM dbo.[Catalog] AS CT
        WHERE CT.[Type] IN (2, 3, 5)";

# Open ADO.NET Connection with Windows authentification.
$con = New-Object Data.SqlClient.SqlConnection;
$con.ConnectionString = "Data Source=$serverName;Initial Catalog=$databaseName;Integrated Security=True;";
$con.Open();

Write-Output ((Get-Date -format yyyy-MM-dd-HH:mm:ss) + ": Started ...");

# New command and reader.
$cmd = New-Object Data.SqlClient.SqlCommand $sql, $con;
$rd = $cmd.ExecuteReader();

$invalids = [System.IO.Path]::GetInvalidFileNameChars();
# Looping through all selected datasets.
While ($rd.Read())
{
    Try
    {
        # Get the name and make it valid.
        $name = $rd.GetString(0);
        foreach ($invalid in $invalids)
            {    $name = $name.Replace($invalid, "-");    }

        If ($rd.GetInt32(1) -eq 2)
            {    $name = $name + ".rdl";    }
        ElseIf ($rd.GetInt32(1) -eq 5)
            {    $name = $name + ".rds";    }

        Write-Output ((Get-Date -format yyyy-MM-dd-HH:mm:ss) + ": Exporting {0}" -f $name);

        $name = [System.IO.Path]::Combine($outputDirectory, $name);

        # New BinaryWriter; existing file will be overwritten.
        $fs = New-Object System.IO.FileStream ($name), Create, Write;
        $bw = New-Object System.IO.BinaryWriter($fs);

        # Read of complete Blob with GetSqlBinary
        $bt = $rd.GetSqlBinary(2).Value;
        $bw.Write($bt, 0, $bt.Length);
        $bw.Flush();
        $bw.Close();
        $fs.Close();
    }
    Catch
    {
        Write-Output ($_.Exception.Message)
    }
    Finally
    {
        $fs.Dispose();
    }
}

# Closing & Disposing all objects
$rd.Close();
$cmd.Dispose();
$con.Close();
$con.Dispose();

Write-Output ((Get-Date -format yyyy-MM-dd-HH:mm:ss) + ": Finished");
```

# SSRS Links

How to Customize the SSRS Report Viewer: [https://github.com/MagnusJohansson/CustomSSRSReportViewer](https://github.com/MagnusJohansson/CustomSSRSReportViewer)

# SSRS Server Pages

This is a bunch of rolling notes for SSRS.

#### Web Portal URL

The standard Web portal UI is here: [https://hostname/Reports](https://hostname/Reports)

Use this link to browse, manage, and view reports with the standard web interface.

#### Report Server URL

The Report Server is available here: [https://hostname/ReportServer](https://hostname/ReportServer)

Use this link to access the report server's backend, or in applications like Report Builder for deploying and managing reports.

#### Reporting Services Configuration Manager

This is a Windows executable, that provides high-level configuration for the report server.

It can be found in the Start menu, as *Reporting Services Configuration Manager*.

#### URL Reservations

Once configured in RSCM, URL reservations are stored in the RSReportServer.config file.  
It is available here: "c:\\Program Files\\Microsoft SQL Server Reporting Services\\SSRS\\ReportServer\\RSReportServer.config".

# SSRS SSL Certificates

Here's some notes on how to add an SSL certificate to SSRS.

#### Certificate Conversion

When importing an SSL certificate (cer) with a separate private key (key), it needs to be in PFX format, for Windows to accept it.

Follow this page to convert a key and cer pair to pfx: [Create PFX Cert File (for IIS)](https://wiki.galaxydump.com/link/217)

The conversion is pasted, here, for clarity:

```bash
"C:\Program Files\Git\usr\bin\openssl.exe" 
pkcs12 -export 
-out myhostname.companyname.com-cert.pfx 
-inkey myhostname.companyname.com.key 
-in myhostname.companyname.com.cer
```

<p class="callout warning">NOTE: The above is displayed on multiple lines for readability.  
To execute, it must be all on one line.</p>

#### Import to Cert Store

Once the PFX is generated, you can import it to the Windows Certificate Store.

For SSRS, import the pfx file to the Local Machine/Personal folder of the certificate store.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-08/scaled-1680-/qeEWMlbLDLwar9FE-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-08/qeEWMlbLDLwar9FE-image.png)

#### Certificate Binding

Once the certificate is in the store, you can bind it in SSRS.

Open the Report Server Configuration Manager from the Start menu.

From the left tree, click on Web Portal URL.

Click the Advanced button, and locate the lower pane for https bindings.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-08/scaled-1680-/5UfH6gVxrEj8fLO9-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-08/5UfH6gVxrEj8fLO9-image.png)

Remove any existing SSL binding, by selecting it and hitting Remove.

The old binding may not successfully delete.

So you can open an elevated command line, and delete the old cert binding, with this:

```
netsh http delete sslcert ipport=0.0.0.0:443
```

Return to the Report Server Configuration Manager, and add the new cert binding.

Select the imported certificate (from earlier), and bind it to port 443.

<p class="callout info">NOTE: Both Web Service URL and Web Portal URL must have the same origin.  
So, be sure to do the same addition to the Web Service URL tab.</p>

You should be able to access the report server URL.

# Code Signing Token Setup



# Adding a HSM Token to a VM

There are a couple of issues when using a SafeNet token from inside a VSphere virtual machine.

### USB Controller

First, we must have a v3.0 USB controller configured for the virtual machine. A v2.0 USB controller has been tried, and not worked.  
If both v2.0 and v3.0 USB controllers are configured it is not certain that the USB-Passthrough logic will reliably the 3.0 controller for the USB key.  
So, be sure to only configure a v3.0 USB controller.

### USB Device

The USB token must be attached to the ESX host, and be visible by it.  
This can be checked by opening an SSH session to the ESX host and performing the command, lsusb.  
The token should appear in the list, like this one:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/ggHOmgX397Nb8ogu-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/ggHOmgX397Nb8ogu-image.png)

### USB Passthrough Problems

By default, ESXi &gt;= 6.5 will not permit pass through connection of CCID USB devices such as the Aladdin Knowledge Token JC to the guest VM.  
This is because smartcard pass through is disabled by default to support DCUI login with smart cards.  
So, HSM tokens such as Aladdin Knowledge Token JC, or SafeNet tokens will encounter errors when mapping them into a virtual machine on VSphere.

NOTE: CCID (chip card interface device) is a USB protocol that allows a smartcard to be connected to a computer via a card reader using a standard USB interface.

The problem presents itself as an error on connect attempt:

Failed to reconfigure virtual machine Win10x64. Cannot connect 'vid:0529 pid:0620 path:0/1/7/1' to this virtual machine. The device was not found.

To correct this…

#### **Enable USB CCID Device Connection in VM**

1.Shut-down the VM  
2.VM &gt; Edit settings &gt; VM Options  
3.Advanced &gt; Configuration Parameters &gt; Edit Configuration  
4.+ Add Parameter and add following Key and Value  
 usb.generic.allowCCID TRUE

#### **Connect Aladdin Knowledge Token JC to VM**

1.Power-up VM  
2.VM &gt; Edit settings &gt; Virtual Hardware  
3.Add other device &gt; USB device  
4.Select Aladdin Knowledge Token JC &gt; Save

Or, the VMX file can be hand-edited with the following steps to add this line to the VMX file of the virtual machine:

usb.generic.allowCCID = "TRUE"

1. Power down the guest VM where it will connect.
2. Remove the guest VM from inventory.
3. Download the vmx file of the guest VM from the datastore where it resides.
4. Open the VMX file in a text editor, and add the following line:  
    usb.generic.allowCCID = "TRUE"
5. Save the updated file.
6. Upload the vmx file back to the datastore.
7. Register the VM in inventory.
8. Power up the VM, and confirm the token is available in the guest OS.

# Code Signing Cert Setup

Each time you receive a new code signing certificate on a USB eToken, there are steps to perform and information to collect, so it can be utilized in an automated build pipeline.

There is little comprehensive documentation available on the web, for how to easily utilize a USB eToken for code signing.  
This may simply be that such information needs to be obscured.  
Or, that it is just not commonly used enough because it’s not something “mortals” and casual programmers do.

What information is available is scattered and inconsistent.  
So, this page attempts to consolidate those steps and list the data required to collect for a build signing tool (to access and use the token).

<p class="callout info">NOTE: The certificate private key of your code-signing certificate cannot be exported from the USB token.  
So, all code-signing operations will require the USB eToken be attached to the build machine, and the SafeNet Authentication Client software be installed and active.</p>

The code-signing certificate does with within a VSphere virtual machine, and additional steps are here: [Adding a HSM Token to a VM](https://wiki.galaxydump.com/link/435)

### Precautions

1. The SafeNet Authentication Client software is designed for use by token creators (certificate vendors), not token users (certificate users).  
    And, there is no functionality available to backup or restore the private key on a USB eToken.  
    So, plenty of care and caution are necessary as the client software contains many functionalities that can reset and render a token inert, or overwrite a certificate, without any safeguards.
2. Above all else, do NOT execute a function called, Initialize Token. This will erase the certificate contents from your eToken, and you will have to buy a new certificate.
3. The USB eToken is configured to enter a lockdown state after a number of failed password attempts.  
    And, SafeNet Authentication Client software has no reliable means to verify that a token password is correct.  
    So, you need to verify that the token password is good using a signing tool.

### Recommendations

There are not many actions (in the client software) required to properly use the USB eToken in automated signing.  
All necessary interactions are listed below.

And since there is no means to backup/restore a code-signing certificate, it is both unnecessary and NOT recommended to explore the SafeNet Authentication Client software outside of the steps below.

#### Results

The below steps will create/retrieve all data elements required by an automated build signing tool.  
Here’s the list of data elements that will be recovered:

- Certificate File Path - location of the certificate’s public key file
- Token Password - required to access the token and use it for signing purposes
- Cryptographic Service Provider (CSP) - used by the build tool to identify the driver of the USB eToken
- Reader Name - Used by the build signing tool to identify the token. Not required if only one token is attached
- Container Name - used by the build signing tool to identify the private key of the code signing certificate

## Required Steps

Here’s a compiled list of actions that must be done, to setup a new HSM token:

- Install SafeNet Authentication Client Software
- Fix CCID Issue (if signing within a VM)
- Check Token Presence
- Create New Token Password
- Export Signing Certificate
- Configure Authentication
- Record Identifying Parameters
- Verify Signing

Steps to follow

#### Install SafeNet Authentication Client Software

This will install the SafeNet Authentication Client Software.

Download the latest version of the SafeNet Authentication Client software from the vendor website.

<p class="callout info">NOTE: The current version, as of 20211228, is V10.8 R6. It has been cached, [here](https://192.168.1.227/svn/OGA/ICoreApp/SafeNet%20Software%20for%20Code%20Signing%20Token "https://192.168.1.227/svn/OGA/ICoreApp/SafeNet Software for Code Signing Token").</p>

#### Fix CCID Issue

Once installed, we need to fix the CCID Issue (when inside a VM).

This is a problem associated with VMWare VSphere, where a USB Safenet token attached to the ESX host does not pass-through correctly to a guest VM.

The workaround for this is, [Adding a HSM Token to a VM](https://wiki.galaxydump.com/link/435 "https://wiki.galaxydump.com/link/435")

#### Check Token Presence

To check the presence of the eToken, do the following:

1. 1. Make sure the eToken is attached to the machine (or connected via USB-Passthrough to the VM).
    2. Double-click the SafeNet Authentication Client in the task bar.  
        [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/cNDukJVOUydO7Z9A-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/cNDukJVOUydO7Z9A-image.png)
    3. If present, the token should appear in the left-hand pane of the client window.  
        [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/J0fWaPD8qw1uHidk-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/J0fWaPD8qw1uHidk-image.png)

#### Create New Token Password

When a token is issued, you are given an initial token password.

This password is used to access your token.  
This password should be changed to ensure no one has intercepted it along the way.  
This password should be a very complex password. And, it is not required to memorize it, because it will be stored in a build automation tool.

To change the token password, do the following:

1. Formulate a new password to be used.  
    It must exceed the complexity requirements (at least 8-characters, have upper and lower case letters, numbers, and special characters).  
    Using a tool, such as LastPass Generator, or an online high-entropy password tool are useful for this.
2. Open the SafeNet client.
3. Select Change Token Password, and enter the new password at the dialog:  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/BwqkDboXctfAA5d9-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/BwqkDboXctfAA5d9-image.png)

#### Export Signing Certificate

A build signing tool requires a copy of the code signing key’s certificate.  
So, we need to export it to a location that we can pass to the build signing tool.  
Do the following to export the certificate:

1. Open the SafeNet Client.
2. Select the Gear Icon to change to Advanced Mode.  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/wFxc8y3KRLbUyGk3-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/wFxc8y3KRLbUyGk3-image.png)
3. This will switch the client to its advanced view, and present a tree-view menu, like this.  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/xOrMtZ3f8WqhLj0f-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/xOrMtZ3f8WqhLj0f-image.png)  
    The Tokens node of this menu, lists all tokens connected to the machine, with the certificate under the CC certificates node.
4. Expand the CC certificates node, to see the actual code-signing certificate, like this.  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/qmt4EFdnOoZYwlbt-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/qmt4EFdnOoZYwlbt-image.png)
5. Right-click the certificate, and select the Export Certificate, to save the public certificate to a file.

<p class="callout info">NOTE: This file is required by an automated build signing tool. So, save it in an accessible path.</p>

#### Configure Authentication

In order for an automated build tool to use the code signing certificate many times over long duration, we must adjust the authentication configuration for the token and software to allow single-sign on and to not Auto-logoff with inactivity.

<p class="callout info">NOTE: This step may not be necessary since we provide the token password in command line arguments to the build tool.  
Further testing is needed to see if this step can be omitted.</p>

1. Open the Advanced View in the SafeNet Authentication Client.
2. Click on Client Settings and the Advanced Tab.
3. Check the boxes for Enable Single Logon and for Enable Single Logon for PKCS#11.  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/L4uPDAj7HMB4QsF6-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/L4uPDAj7HMB4QsF6-image.png)
4. Click Save to save settings.
5. The documentation says this change requires a session update to take effect.  
    But, this doesn’t seem to always be the case.  
    And, we need to also disable auto-logoff in the next step. So, we will continue, and reboot after that.
6. Make sure the Automatic Logoff duration is set to Never.  
    This ensure we will remain logged into the token for a long-duration.
7. Reboot the machine to ensure the changes take effect.

#### Record Identifying Parameters

An automated build tool requires a few parameters to access and use the private key of the code signing certificate.

These are the “CSP”, “Reader Name”, and the “Container Name”, and are all accessible from the SafeNet Authentication Client.

Here’s a list of steps to retrieve each of these:

1. To get the Reader Name, switch to the Advanced view, and click on the token node (under the Tokens list).  
    <p class="callout info">NOTE: The Reader Name is only required if you have more than one reader connected. If you have only one token, you can skip this step.</p>
    
    The Reader Name is listed as a parameter in the right-hand pane. It must be copied out, exactly as written.  
    The best way to retrieve the Reader Name parameter is to use the Copy-To-Clipboard function on the toolbar:  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/FdJouk2kqAqgS6Ge-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/FdJouk2kqAqgS6Ge-image.png)
2. Then, paste the clipboard contents into notepad and recover the Reader Name, there.
3. To get the Container Name, stay in the Advanced view, and click on the individual certificate (under the Certificates node).  
    The Container Name is a parameter of the private key in the right-hand pane. It must be copied out, exactly as written.  
    It can be retrieved, same as the Reader Name, by using the Copy-To-Clipboard function on the toolbar:  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/h4Ae9ZQX7tG8Ytbe-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/h4Ae9ZQX7tG8Ytbe-image.png)
4. Then, paste the clipboard contents into notepad and recover the Container Name, there.
5. To get the CSP (Cryptographic Service Provider) name, stay in the Advanced view, and click on the token node in the left-hand pane.
6. In the right-hand pane, scroll to the bottom of the token’s parameter list, and locate the CSP parameter. It must be copied out exactly as written.  
    It can be retrieved, same as the other parameters, by using the Copy-To-Clipboard function on the toolbar:  
    [![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/LzhIPYTkvIKT7HCG-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/LzhIPYTkvIKT7HCG-image.png)
7. Then, paste the clipboard contents into notepad and recover the CSP name, there.
8. Once the CSP, Reader Name, and Container Name are retrieved, they need to be added to the build signing tool configuration, along with the token password, and the path to the signing certificate file.

#### Verify Signing

<p class="callout warning">Warning: A USB eToken will enter a lockdown state if too many failed password attempts have been made (requiring a certificate-vendor support to unlock).</p>

And, the SafeNet Authentication Client software has no reliable means to quickly verify that a token password is correct.  
The token must be used, to check the password.

So, any developed logic that orchestrates the build signing tool must include functionality to perform a single-step check of the token password. This will allow you to verify the token password works with a new token, along with verifying that other parameters (Reader and Container) are good to use.

1. With all the previous steps executed and data collected, we must perform a single-step signing to verify the token password, Reader Name, and Container Name are all correct.

# Connecting to SSH Server from Windows

Here’s a list of steps to connect with an SSH server from a Windows PC using SSH key authentication.

Open the Putty application, Putty.exe.

From the GUI, set the hostname and port of the SSH service, and come up with a memorable name for the session, and save it:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/buS90CcOSrXInqwq-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/buS90CcOSrXInqwq-image.png)

Next, drill down the left-hand tree to the Data page under Connection, and set the Auto-Login username to the username the SSH key belongs to:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/eVq1RVXlg3LOtKqT-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/eVq1RVXlg3LOtKqT-image.png)

Navigate to the Auth page at Connection/SSH/Auth, and set the Private Key file path:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/e4E3FHow2CyFuXR3-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/e4E3FHow2CyFuXR3-image.png)

Go back to the top of the tree, and click Session. Then, save the session, same as before, to capture the updated configuration.

Now, the connection can be tested.

Load the saved session, and click Open, to attempt a new connection with the SSH server.

If successful, it will login and present a regular terminal prompt, same as would happen during password authentication.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/ddMJvKM4WdHWvIrw-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/ddMJvKM4WdHWvIrw-image.png)

The server will display the name of the used key, to indicate SSH key authentication was used.

# HashiCorp Vault



# Generate Certificates with Hashicorp Vault

Here are steps to generate SSL certificates using HashiCorp Vault as an Intermediate CA.

NOTE: Be sure that you've setup a vault instance as an Intermediate CA.  
See this page for how: [Vault as Intermediate CA](https://wiki.galaxydump.com/link/455)

Login to the web UI of your intermediate CA, such as: [https://vault02.ogsofttech.lan:8200/ui/](https://vault02.ogsofttech.lan:8200/ui/)  
If DNS is down, use this: [https://192.168.60.6:8200/ui/](https://192.168.60.6:8200/ui/)

For the latest intermediate CA url, see this page: [Vault Services](https://wiki.galaxydump.com/link/300)

Find the issuing role by navigating to Secrets/PKI/Roles.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/Ctq7LL1OQADcWAZf-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/Ctq7LL1OQADcWAZf-image.png)

Select the role, and click Generate Certificate:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/ZJgTJD3X6MrCO68T-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/ZJgTJD3X6MrCO68T-image.png)

Fill in the Common name as: router.ogsofttech.lan.

Set the TTL to 1 year (365 days).

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/w26rZjkslfBLo9UP-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/w26rZjkslfBLo9UP-image.png)

Click Generate, to create the key and certificate, and you’ll see this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/0Lso9nwbaxRntk1h-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/0Lso9nwbaxRntk1h-image.png)

Download the private key as: router.ogsofttech.lan-key.pem

Download the certiticate as: router.ogsofttech.lan-cert.pem

Download the CA chain as: router.ogsofttech.lan-cabundle.pem

<p class="callout info">NOTE: We are calling the downloaded CA chain file a “ca bundle”.  
CA bundle is the standard naming convention for this file type.  
Specifically, a cert is often concatenated with the CA bundle that signed it, to create a chain certificate file.</p>

Now, you can copy the cert, ca bundle, and private key to the host, for usage.

If generating a pair for a linux host, you will need them as .crt and .key files.  
Follow this: [Converting PEM to crt and key](https://wiki.galaxydump.com/link/303 "https://wiki.galaxydump.com/link/303")

If generating a pair for an Nginx host, you will need

# Hashicorp Vault Setup

Here are steps for setting up a secrets store using Hashicorp Vault, on Ubuntu 24.

## References

Lots of steps were taken from here: [https://developer.hashicorp.com/vault/tutorials/secrets-management/pki-engine](https://developer.hashicorp.com/vault/tutorials/secrets-management/pki-engine)

## Server Setup

Before installing Vault, perform steps from this page, to setup the server: [Ubuntu Host Setup](https://wiki.galaxydump.com/link/245)

Since a typical vault cluster has limited internet visibility, it may be necessary to map in the local NTP server, to keep each node in sync.  
See this page for how to setup each host/VM to use the local private NTP server: [Ubuntu: Use Private NTP Server](https://wiki.galaxydump.com/link/457)

### Install Vault

Here are three lines to install vault.

First, we will setup the package source keys…

```
sudo wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
```

Now, add the package source entry…

```bash
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
```

Now, install vault…

```bash
sudo apt update && sudo apt install vault
```

### Vault User

The installer created a user called, vault, and the service runs under it.  
But, the default keys are restricted to root permissions.  
So, we need to fix access to certificates.

For this, we will create a group to make it easier to control and test access to certs and keys.

```bash
sudo groupadd pki
```

And, we will add the vault user to the group:

```bash
sudo usermod -a -G pki vault
```


#### Clustered Config

<p class="callout info">NOTE: If you are standing up a single node vault, skip this section.</p>

Work through the steps on this page, for clustering your vault servers: [Clustering HashiCorp Vault](https://wiki.galaxydump.com/link/448)

### Vault Configuration

<p class="callout info">NOTE: This is for a single vault node. If you are standing up a cluster, skip this section.</p>

Edit the config file at: /etc/vault.d/vault.hcl

Specifically, you want to set the tls cert/key paths to your own domain certs.

As well, you may want to limit the listener to just a single address, since the default is for all adapters on the host.

Once done, save the config update.

#### Continue Setup

Once installed and configured, we need to enable and start the vault services, with these lines:

```bash
sudo systemctl daemon-reload
sudo systemctl enable vault
sudo systemctl start vault
sudo systemctl status vault
```

You should be able to verify that the vault service is active by call this:

[https://&lt;host-ip&gt;:8200/v1/sys/seal-status](https://192.168.60.101:8200/v1/sys/seal-status "https://192.168.60.101:8200/v1/sys/seal-status")

<p class="callout info">NOTE: Use your VM’s fully-qualified name (or IP address), in the above.</p>

If successful, the above call will return something like this:

```json
{
  "type":"shamir",
  "initialized":false,
  "sealed":true,
  "t":0,
  "n":0,
  "progress":0,
  "nonce":"",
  "version":"1.17.6",
  "build_date":"2024-09-24T19:48:40Z",
  "migration":false,
  "recovery_seal":false,
  "storage_type":"file"
}
```

### Cluster Unseal

If you're standing up a cluster, see this page: [HashiCorp Vault Cluster Unseal](https://wiki.galaxydump.com/link/449)

If you're standing up a single-node vault instance, see this page: [Vault Single-Node Unseal](https://wiki.galaxydump.com/link/452)

### Administrative Setup

Once the vault is unsealed, you need to setup auditing and administrative policies.

See this page for how: [Vault Administrative Setup](https://wiki.galaxydump.com/link/453#bkmrk-page-title)

### Creating Users (Access Tokens)

Once the vault cluster is setup, you need to establish some administrative users.

See this page for how to create admin and user tokens: [Vault Token Administration](https://wiki.galaxydump.com/link/451)

To protect tokens in transit, see this page for Response-Wrapped Tokens: [Vault Wrapping Token](https://wiki.galaxydump.com/link/450)

### Login

Once your vault is unsealed, you can log into its UI.

Log into its UI, here: [http://&lt;host-ip&gt;:8200/](http://192.168.60.5:8200/ "http://192.168.60.5:8200/")

You will be prompted to enter your root token, here:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/fCJSlTXqhKlvOLbU-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/fCJSlTXqhKlvOLbU-image.png)

If successful, you will see the root user’s dashboard:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/8RcEWTlkuNOheZ78-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/8RcEWTlkuNOheZ78-image.png)

### Creating the First Secret Store

Now, you should create a secrets engine, to store things.  
The one that makes the most sense, first, is to make a KV store.  
You should be able to make a kv secrets store (actually, a kv-v2 store) from the dashboard:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/IJ5OkCQNHCb7zzMP-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/IJ5OkCQNHCb7zzMP-image.png)

The above will create the secrets store at the path: /kv  
You can change this if needed.

Once created, you will see the new empty store:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/Hl1aX8MoCi5VMGQk-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/Hl1aX8MoCi5VMGQk-image.png)

### Using Vault as a CA

Follow steps on this page, to setup a vault instance as a Root CA: [Vault as Root CA](https://wiki.galaxydump.com/link/454)

<p class="callout info">NOTE: You never want to directly sign certificates with a Root CA.  
If you do, there is no way to revoke the root CA, without collapsing all PKI chains (by revoking the root CA certificate).  
However. You can create an Intermediate CA that does signings.  
And, that Intermediate CA can be revoked, without losing the root CA certificate.</p>

<p class="callout info">So, we will create an Intermediate CA, to do actual signings.  
And, the Intermediate CA's certificate will be signed by the Root CA, just before offlining the Root CA.</p>

Once a root CA is setup, you can follow this page, to setup an Intermediate CA,  
that will do actual signings: [Vault as Intermediate CA](https://wiki.galaxydump.com/link/455)

See this page for how to generate certificates: [Generate Certificates with Hashicorp Vault](https://wiki.galaxydump.com/link/433)

# Clustering HashiCorp Vault

Here are special instructions for setting up a vault cluster.

<p class="callout info">NOTE: See the regular setup page for other details: [Hashicorp Vault Setup](https://wiki.galaxydump.com/link/434#bkmrk-page-title)</p>

#### DNS Resolution

Since the vault services will communicate with eachother over TLS, they will need certificates.  
And as such, the certs will include hostnames.

So, open the /etc/hosts file of each vault host, and add entries, at the bottom of the file, for each instance and API host.

Here's an example list of entries for a cluster:

```
192.168.75.10 vault02api
192.168.75.21 vault0201
192.168.75.22 vault0202
192.168.75.23 vault0203
192.168.75.24 vault0204
192.168.75.25 vault0205
192.168.75.26 vault0206
```

#### Filesystem Changes

##### Raft Folder

The Vault service will be running Raft. So, it will need a folder for the Raft backend.

<p class="callout info">NOTE: This may mean that the folder /opt/vault/data is obsolete.  
But, we will not worry about that, for now.  
The 'data' folder was created by the installer as the FS location for a storage = 'file' backend.</p>

Create the raft folder with these:

```
sudo mkdir /opt/vault/raft
```

```
sudo chmod 700 /opt/vault/raft
```

```
sudo chown vault:vault /opt/vault/raft
```

TLS Folder

The installer already created the TLS folder, to store certificates.  
It is at: /opt/vault/tls

We will leave it as is.

Config Folder

The installer created a config folder at: /etc/vault.d

We need to bolster its permissions, as it may contain seal stanzas.

Update permissions of the config folder with these:

```
sudo chmod 0750 /etc/vault.d
```

```
sudo chown root:vault /etc/vault.d
```

#### Firewall Rules

Update the local firewall rules for each vault host, to allow 8200 and 8201 access.

```bash
sudo ufw allow 8200
```

```bash
sudo ufw allow 8201
```

#### Certificates

We will create certificates for each vault instance, and put them in the tls folder at: /opt/vault/tls

Follow instructions, here, to generate certificates for each host: [Generate Certificates with Hashicorp Vault](https://wiki.galaxydump.com/link/433)

<p class="callout info">NOTE: Be sure to do the following:  
- Set the common name to the fully qualified name: ex: vault0204.ogsofttech.lan.  
- Set the expiry to two years (17520 hours).  
- Set set the SAN IP to the address of the host: 192.168.75.24</p>

Copy the CA certificate bundle (CA chain of issuer and root CA) into: /opt/vault/tls/ca.crt

<p class="callout info">NOTE: The ca.crt file should be the certificate bundle of issuer CA cert and root CA certificate.  
These should be bundled (root + intermediate) in the ca.crt file, as a concatenated PEM.</p>

Concatenate the vault service certificate with the issuer CA cert as a concatenated PEM.

Paste the certificate file (vault service + issuer CA cert) into: /opt/vault/tls/vault.crt

<p class="callout info">NOTE: The vault.crt file should include the leaf certificate (of the node) plus the signing intermediate.</p>

Copy the vault service private key into: /opt/vault/tls/vault.key

Once key and certs are stored, we need to set permissions on the files, with these:

```bash
# Ownership: keep vault:vault
sudo chown vault:vault /opt/vault/tls/vault.crt /opt/vault/tls/vault.key /opt/vault/tls/ca.crt

# Permissions:
# private key: only vault access
sudo chmod 0600 /opt/vault/tls/vault.key
# server cert usually fine as world-readable
sudo chmod 0644 /opt/vault/tls/vault.crt
# CA cert usually fine as world-readable
sudo chmod 0644 /opt/vault/tls/ca.crt
```

Here's a quick sanity check, to verify the certificates on a host:

```bash
# Verify the server chain file against your trust bundle
openssl verify -CAfile /opt/vault/tls/ca.crt /opt/vault/tls/vault.crt

# See what the gateway/clients will see
openssl s_client -connect vault0204:8200 -showcerts -verify_return_error \
  -CAfile /opt/vault/tls/ca.crt </dev/null
```

#### Vault.HCL Changes

Now, we need to create a config file for each vault service (vault.hcl).

There will be some tailoring required for each host.  
So, pay attention to the notes.

Open the vault config file at: /etc/vault.d/vault.hcl

```bash
sudo nano /etc/vault.d/vault.hcl
```

Below is a config file for a single node in a vault cluster.

<p class="callout info">NOTE: You will need to change the following lines for each host:  
 api\_addr  
 cluster\_addr  
 node\_id</p>

```json
# Vault Configuration for Clustering.
# Created on 20250901

ui            = true
cluster_name  = "vaultcluster2"
# recommended with Integrated Storage
disable_mlock = true

# Advertise addresses (MUST be correct & reachable)
# Define the external API address that clients will use to communicate with this Vault node.
# set per node
api_addr     = "https://vault0204.ogsofttech.lan:8200"
# Define the internal address used by Vault nodes to communicate with this Vault node.
# Vault ignores the scheme of this URL, so it doesn't matter if http or https.
# set per node (node's intra-cluster IP:8201)
cluster_addr = "https://192.168.75.24:8201"

listener "tcp" {
  address                  = "0.0.0.0:8200"
  tls_disable              = 0
  tls_cert_file            = "/opt/vault/tls/vault.crt"
  tls_key_file             = "/opt/vault/tls/vault.key"
  tls_client_ca_file       = "/opt/vault/tls/ca.crt"
  # optional: require client certs from automation:
  # tls_require_and_verify_client_cert = "true"
}

# Updated to use raft backend (Integrated Storage).
storage "raft" {
  path    = "/opt/vault/raft"
# unique on each node  
  node_id = "vault0204"

  # Auto-join peers (preferred over manual joins)
  retry_join {
    leader_api_addr      = "https://vault0204.ogsofttech.lan:8200"
    leader_ca_cert_file  = "/opt/vault/tls/ca.crt"
  }
  retry_join {
    leader_api_addr      = "https://vault0205.ogsofttech.lan:8200"
    leader_ca_cert_file  = "/opt/vault/tls/ca.crt"
  }
  retry_join {
    leader_api_addr      = "https://vault0206.ogsofttech.lan:8200"
    leader_ca_cert_file  = "/opt/vault/tls/ca.crt"
  }
}

# Recommended health endpoint behavior for LBs
# (defaults are fine; LB should treat 200 as active, 429 as standby, 503 sealed)
# See /v1/sys/health docs for details.

```

### Service File (Systemd Unit)

With folders fixed, certs and config defined, we need to configure the vault service for operation.

To do so, we need to identify the service file that the installer created, with this:

```bash
sudo systemctl cat vault.service
```

As of 20250803, the installer puts the systemd unit file, here:

```
/usr/lib/systemd/system/vault.service
```

We need to open it and update it:

```
sudo nano /usr/lib/systemd/system/vault.service
```

Once updated, save and close the systemd file.

Return to the generic vault setup page: [https://wiki.galaxydump.com/link/434#bkmrk-continue-setup](https://wiki.galaxydump.com/link/434#bkmrk-continue-setup)

# HashiCorp Vault Cluster Unseal

These steps are for a new vault cluster that has been configured and started up, but is in an unsealed state.

For an existing cluster with unsealed nodes, see this page: [Handling Vault Node Restart](https://wiki.galaxydump.com/link/458)

### Leader Initialization

The following will initialize a new vault cluster and return a set of unseal keys and a root token.

<p class="callout info">NOTE: The ca.crt file is privileged, You will need to run these commands as the root user.  
Run the following to switch to the root user:</p>

```bash
sudo -i
```

Go to the first node, and do these (as root):

```bash
# From an admin shell that can reach the VLAN:
export VAULT_ADDR="https://vault0204.ogsofttech.lan:8200"
export VAULT_CACERT="/opt/vault/tls/ca.crt"   # path on your admin box

# Initialize the cluster (choose your own shares/threshold)
vault operator init -key-shares=5 -key-threshold=3
```

<p class="callout info">NOTE: Use the fully-qualified hostname above, as it appears in the node's cert.</p>

Once executed, the vault node will reply with 5 unseal keys and an initial root token.

<p class="callout warning">Distribute each of these unseal keys to trusted admins, to store in offline password storage.</p>

<p class="callout info">NOTE: Three (3) unseal keys are required to unseal the vault.</p>

<p class="callout info">Use the initial root token to setup policies and auth.  
Then, retire it.</p>

### Unseal the Leader

With the unseal keys from the initialized node (received above), we need to unseal its vault.

<p class="callout info">NOTE: We do this, while still as root, and on the same host that we got the keys from.</p>

Now, unseal each node, by calling this command once each, for three of the five unseal keys:

<p class="callout info">NOTE: It will prompt you for the unseal key, each time you run it.</p>

```bash
vault operator unseal
```

### Initial Root Login

With the vault unsealed, we need to perform an initial login as root:

```bash
# Log in with the root token for initial setup tasks
vault login <root_token>
```

Once logged in, you can check the vault status with this:

```bash
vault status
```

If successful, you should see `Initialized: true`, `Sealed: false`, `HA Enabled: true`, and this node as leader.

The first node is online, and the cluster is up... sort of.

Each cluster member has auto-discovered a leader and established a RAFT quorum.

But, the other nodes are still not unsealed (since we did not configured auto-unseal).

### Unseal Other Nodes

Similar to what you did, to unseal the first node, we will do the same to each member, below.

Switch to root on each node with:

```bash
sudo -i
```

Set exports for each node:

<p class="callout info">NOTE: Make sure that the vault\_addr variable is pointing to the local node being unsealed, here.</p>

```bash
export VAULT_ADDR="https://vault0205.ogsofttech.lan:8200";
export VAULT_CACERT="/opt/vault/tls/ca.crt"
```

<p class="callout info">NOTE: Use the fully-qualified hostname above, as it appears in the node's cert.</p>

Now, unseal each node, by calling this command once each, for three of the five unseal keys:

<p class="callout info">NOTE: It will prompt you for the unseal key, each time you run it.</p>

```bash
vault operator unseal
```

### Check Status

Once you have initialized the leader node, and unsealed all nodes, we need to confirm that the cluster is good.

Run this:

<p class="callout info">NOTE: The vault\_addr should be pointing to the leader, here.</p>

```bash
# Set this if you are coming back to this page, and the environment value is not set...
export VAULT_ADDR="https://vault0204.ogsofttech.lan:8200";
# Run this to check status...
vault status
```

Confirm RAFT peers with this:

```bash
vault operator raft list-peers
```

<p class="callout warning">NOTE: The above may only work on the current leader, because https.  
We need to work through why this is, and solve it, so it can be run on any node.</p>

When run, you will see something like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/HCX5VSa7OOY5gn5s-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/HCX5VSa7OOY5gn5s-image.png)

If healthy, you will see one node as leader, and the others as voting followers.

<p class="callout info">NOTE: Make sure each node you configured, is present.</p>

There is a health check that can be performed, by calling this:

```bash
curl -s -H "X-Vault-Token: $VAULT_TOKEN" "$VAULT_ADDR/v1/sys/ha-status" | jq .
```

<p class="callout info">NOTE: Be sure that the VAULT\_TOKEN and VAULT\_ADDR environment variables are set.  
Or, you can hardcode them with a minimal privilege user account.</p>

When run, you will get a JSON list of nodes and their status.

# Vault Wrapping Tokens

When creating access tokens for HashiCorp Vault, you always want to prevent them from falling into the wrong hands, or showing up as clear-text in command line history, logs and audit trains.

To reduce the chance of tokens being passed in the clear, you can create a new user token in a response-wrapped state.  
This allows the token to be given to a user, over chat, or text, without much concern.

The response-wrapped token has a very short lifetime, and can only be redeemed once.

Here's how to create a response-wrapped token:

```bash
vault token create -role=admin -orphan -wrap-ttl=5m -format=json
```

The command response will include the wrapping token, like this:

```json
{
  "request_id": "",
  "lease_id": "",
  "lease_duration": 0,
  "renewable": false,
  "data": null,
  "warnings": null,
  "wrap_info": {
    "token": "hvs.tH5Wn8bD3eJxvMq1iP7F",
    "accessor": "PNowpcQP0jJ9Jpz06o2oueYW",
    "ttl": 300,
    "creation_time": "2025-09-04T02:26:49.859843035Z",
    "creation_path": "auth/token/create/admin",
    "wrapped_accessor": "5KlfcQwbo05Ej37YOqBJnfHM"
  }
}
```

The wrap\_info/token property is what you give to the user.

The user can then, redeem their access token by submitting the wrapping token, like this:

```
vault unwrap hvs.tH5Wn8bD3eJxvMq1iP7F
```

Vault will respond with the real token, and revoke further usage of the wrapping token.

<p class="callout info">NOTE: The wrapping token has an expiry. If exceeded, the user will need to request another.</p>

### Wrapped Token Benefits

Using wrapped tokens, prevents exposure in shell history or logs.

Delivery is safer, as you can drop the short-lived wrapper into a config management pipeline or paste in a chat.

The Vault audit log records that the wrapping token was created and who unwrapped it.

# Vault Token Administration

Here are notes on access token administration.

Be sure that you've already setup an administrative policy in your vault cluster.  
See this page for how: [Vault Administrative Setup](https://wiki.galaxydump.com/link/453#bkmrk-page-title)

### Creating Admin Tokens

Once the admin policy exists, you can create administrative access tokens.

To do so, log into the leader note, with this:

```bash
# Use the root token...
vault login <root_token>
```

You can create a simple admin token that lasts 24 hours:

```bash
# Create an admin token that lasts 24 hours...
vault token create -policy=admin -ttl=24h
```

The above will issue short-lived admin tokens, so that the vault is more protected.  
These can be minted by an automated script, that issues token as needed.

### Traditional Admin

A more traditional administrative token needs to be longer-lived than a 24 hour token.

So, we will create a token role for admins.

This will create a periodic, orphan role so that admin tokens can be renewed forever (until revoked), and aren't tied to a parent token:

<p class="callout info">NOTE: You will need to be logged into the vault leader (vault login &lt;token&gt;).</p>

```bash
vault write auth/token/roles/admin \
  allowed_policies="admin,default" \
  orphan=true \
  renewable=true \
  period=720h
```

The above token role, can create tokens that last 30 days, and can be renewed, without changing the token.

With the above admin role, you can create admin tokens of a more traditional lifetime, with this:

```bash
vault token create -role=admin -orphan -format=json
```

The above command will create admin tokens that can be given to your ops group, for longer-lived administrative access.

<p class="callout info">NOTE: Be sure to save the client\_token field in a secure place, such as a private password manager.</p>

### Token Renewal

Before a token expires, you can renew it with this command:

```bash
VAULT_TOKEN=<admin_token> vault token renew
```

<p class="callout info">NOTE: The above can be automated, if the admin token is used by scripts or services.  
(Automate with a small systemd timer/cron on a secure host or in your gateway.)  
</p>

### Creating Other Admins

You can use an admin token to create other admins, like this:

```bash
# Another admin token (same role)
vault login <admin_token>
vault token create -role=admin -orphan -format=json
```

### Creating Tokens for other Groups

Once you have other roles established as policies, you can create tokens for users, in those roles/policies, like this:

```bash
vault token create -policy=kv-readers -ttl=8h
```

### Response-Wrapped Tokens

See this page for Response Wrapped Tokens: [Vault Wrapping Token](https://wiki.galaxydump.com/link/450)

# Vault Single-Node Unseal

Here are instructions on how to unseal a single-node vault cluster.

### Vault CLI Comms

For the vault command to communicate with the running service, we must set an env variable for it:

```bash
export VAULT_ADDR=https://your_domain:8200
```

<p class="callout info">NOTE: You may have to set the above to http, if you haven’t given vault a valid ssl cert, yet.</p>

### Initialize the Vault

In order for the vault service to manage secrets, you must initialize its store.  
To do this, you need to run the following:

```bash
vault operator init -key-shares=3 -key-threshold=2
```

You can change the key shares and threshold based on your risk profile.

The vault CLI will respond with something like this:

```bash
Unseal Key 1: eZcJeydRrqeSMZ1zTN+VVll9TFT2KvJy7VlnxUgtvuz5
Unseal Key 2: ntmqCKq8rgNnKT1YSLCjVmCCZBAA3NwUeqxIyRpYD4Wm
Unseal Key 3: 3FK1+Hsorh4p8/L9mki3VskaEU2eQhLqGOI/pJkTHMbx

Initial Root Token: s.hY0ieybfDqCadz7JpL88uO3x
```

Save these values in a secure location, as this is the only time you will receive them.

Now, you can run the vault status command, and see it has changed to Initialized=true.

Run this to get vault status:

```bash
vault status
```

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/4XqZ3L6xD4ZuobAw-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/4XqZ3L6xD4ZuobAw-image.png)

Now, you need to unseal the vault, so it can be used.

Notice the vault status showed an unseal progress of 0/2.  
This means, that at least two more unseal tokens must be submitted for it to be available for access.

Enter enough tokens to unlock your vault with this command, and paste in one of the tokens when prompted:

```bash
vault operator unseal
```

Once the unseal threshold has been met, the Sealed state will become ‘false’.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/ckVlpmyoclmmlBcy-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/ckVlpmyoclmmlBcy-image.png)

Your vault is available for access.

# Vault Administrative Setup

Once you have a vault instance or cluster unsealed, you can setup auditing, and administrative policies, with these instructions.

See this page for how to setup a vault instance or cluster: [Hashicorp Vault Setup](https://wiki.galaxydump.com/link/434)

### Audit Logging

Create a folder for capturing audit logs on each node (leader and followers):

```bash
sudo mkdir -p /var/log/vault
sudo chown vault:vault /var/log/vault
sudo chmod 0750 /var/log/vault
```

Tell the leader node to store audit logs in the created folder:

```bash
vault audit enable file file_path=/var/log/vault/audit.log
```

### Admin Policy

<p class="callout warning">WARNING: The initial root token has broad privilege, and bypasses all policy checks, being meant for bootstrapping and emergencies.  
You need to create an actual admin policy, so that proper security and auditing can occur. </p>

Here, we will setup an admin policy, so that we can stop using the initial root token.

Doing so, gives us many benefits:

- Users created under the admin policy share the same privileges as the root user.
- We can issue short-lived admin tokens.
- We can audit all admin actions (audit logs will show policy=admin and the user, not just root).
- We can later, reduced privileges, instead of everyone having anonymous root access.

<p class="callout info">NOTE: We only have to create the admin policy once.  
We can do it on the current leader.</p>

Create a file called: admin.hcl:

```bash
sudo nano /etc/vault.d/admin.hcl
```

<p class="callout info">NOTE: It doesn't matter where we generate the admin.hcl file, as the policy write command will pull it into the vault.  
But for simplicity, we will create it on the leader node, so we have easy tracking of what setup steps have been done.</p>

Give the admin policy file, this content:

```json
# --- token management (mint/lookup/renew/revoke/roles) ---
path "auth/token/create"         { capabilities = ["update"] }
path "auth/token/create/*"       { capabilities = ["update"] }
path "auth/token/lookup"         { capabilities = ["read", "update"] }
path "auth/token/lookup-self"    { capabilities = ["read"] }
path "auth/token/renew"          { capabilities = ["update"] }
path "auth/token/renew-self"     { capabilities = ["update"] }
path "auth/token/revoke"         { capabilities = ["update"] }
path "auth/token/revoke-self"    { capabilities = ["update"] }
path "auth/token/roles"          { capabilities = ["list", "read"] }
path "auth/token/roles/*"        { capabilities = ["create","read","update","delete","list"] }

# --- policies (so admins can maintain policies without root) ---
path "sys/policies/acl"          { capabilities = ["list"] }
path "sys/policies/acl/*"        { capabilities = ["create","read","update","delete","list"] }

# --- core admin knobs (optional but typical) ---
path "sys/audit"                 { capabilities = ["read"] }
path "sys/audit/*"               { capabilities = ["create","read","update","delete","sudo"] }
path "sys/auth"                  { capabilities = ["read"] }
path "sys/auth/*"                { capabilities = ["create","read","update","delete","sudo"] }
path "sys/mounts"                { capabilities = ["read"] }
path "sys/mounts/*"              { capabilities = ["create","read","update","delete","sudo"] }

# --- give admin wide access to secrets during bootstrap (tighten later) ---
path "*"                         { capabilities = ["create","read","update","delete","list","sudo"] }

```

Once created, save and close.

Here are some notes about the above policy:

- The top block allows an admin to perform all token management actions.
- The middle block allows admins to manipulate policies.
- The third block allows admins to perform other root actions.
- The bottom block (path "\*") allows access to all paths in the vault (like root). 
    - capabilities = create/read/update/delete/list - These allow full browsing and editing.
    - capabilities = sudo - allows doing system-level actions like: 
        - enabling audit devices,
        - enabling auth methods,
        - mounting new secrets engines,
        - etc.

So, the above admin policy is just like root, but allows us to have named users as admins.

Enable the admin policy with this:

```bash
vault policy write admin admin.hcl
```

Once written, the policy is stored in RAFT, and replicated to all nodes.

We can confirm the policy with this:

```
vault policy read admin
```

Now, any tokens with the admin policy, will function with the above administrative privileges.

### Creating Admin Tokens

Once the admin policy exists, you can create administrative access tokens.

See this page for how to create admin and user tokens: [Vault Token Administration](https://wiki.galaxydump.com/link/451)

To protect tokens in transit, see this page for Response-Wrapped Tokens: [Vault Wrapping Token](https://wiki.galaxydump.com/link/450)

# Vault as Root CA

Here are steps you can follow to setup a vault instance as a Root CA.

<p class="callout info">NOTE: This page assumes that you have created a single-node vault instance to serve as your Root CA.  
See this page for how to do that: [Hashicorp Vault Setup](https://wiki.galaxydump.com/link/434)</p>

<p class="callout info">NOTE: These steps will create a root CA with one signing key.  
You should create an intermediate CA, as well, that will perform the actual signing of certs.  
This will allow you to offline this root CA, once the intermediate CA is up.  
</p>

### Starting the PKI Engine

<p class="callout info">NOTE: From here down, is steps for creating a Root CA.  
If you are setting up an Intermediate CA, skip to ‘Configure Intermediate CA’.</p>

For Vault to serve as a root CA, you have to add the PKI secrets engine.

To do this, enable the pki secrets engine:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/BJEMdmaa924Dxt4l-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/BJEMdmaa924Dxt4l-image.png)

For a Root CA, set the Max lease time to as long as possible, as the root CA will be offlined after generation.

Once initialized, it will look like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/7UXBd9LqRWxNChmF-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/7UXBd9LqRWxNChmF-image.png)

For a root CA, click Configure PKI to begin setup.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/5QsHHgxFSMzVfK24-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/5QsHHgxFSMzVfK24-image.png)

Choose the Generate Root option.

Set Type to Internal.

Set your Common name. Usually, this is a domain with a private TLD suffix.

Set the issuer name.

Set a TTL that is maxed out: 87600 hours.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/35DUBNK47kciPGoZ-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/35DUBNK47kciPGoZ-image.png)

Scroll down and fill in the issuer URLs, matching the origin for your root CA host:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/JbkGlKLJpFTuB756-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/JbkGlKLJpFTuB756-image.png)

When finished, click Done, to generate the root CA key and certificate.

You will see a page like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/3WbleXpXtzVMm7aI-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/3WbleXpXtzVMm7aI-image.png)

Copy out the root CA certificate, and save it to a file, named: `ogsofttech.lan_ca.crt`

Install it on all machines of the local network, so they will recognized the signed SSL certs of hosts.

### Current Intranet Root CA Certificate

The current root CA for the local intranet can be found in the secure share at this path:

```
\SecureShare git\oga\ogsofttech.lan\rootCA
```

<p class="callout info">NOTE: This certificate should be installed on any host that will consume services signed by the root ca.</p>

See this page for how to install it on an Ubuntu host: [How to Add Root CAs to Ubuntu](https://wiki.galaxydump.com/link/432)

### Root CA Rotation

To make things easier, when it comes time to rotate your root CA key, add a role, now.

Click PKI.

Click Roles.

Click Create Role.

Give the new role a name that you will recognize as the root CA rotation role: CA\_rotation\_role

Leave the rest of the fields empty, and click Create.

Now, you have a working root CA key/cert for your network.

We will use it to sign the certificate of an Intermediate CA, that will do all the actual work.

And, we will offline the root CA.

# Vault as Intermediate CA

Here are instructions for setting up a vault instance as an Intermediate CA.

<p class="callout info">NOTE: This page assumes that you have created a single-node vault instance to serve as your Root CA.  
See this page for how to do that: [Hashicorp Vault Setup](https://wiki.galaxydump.com/link/434)</p>

<p class="callout info">NOTE: Always create the Intermediate CA as a separate instance from your Root CA.  
This allows you to offline the Root CA, to uphold the integrity of your root CA certificate.</p>

To properly sign certificates, you will want to generate an Intermediate CA that will do all the actual issuing.  
And, you will offline the root CA, after the Intermediate CA is usable.

See this page if you need to setup a vault instance as a Root CA: [Vault as Root CA](https://wiki.galaxydump.com/link/454)

### Configure Intermediate CA

For Vault to serve as an Intermediate CA, you have to add the PKI secrets engine.

To do this, enable the pki secrets engine:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/mXT8nc47Ja3TXmiF-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/mXT8nc47Ja3TXmiF-image.png)

For an Intermediate CA, set the Max lease time to 43800 hours, and enable the engine.

Once initialized, it will look like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/ZutfwCBRaLggp7yy-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/ZutfwCBRaLggp7yy-image.png)

For an Intermediate CA, click Configure PKI to begin setup.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/LB4ciTg9hATmaz9k-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/LB4ciTg9hATmaz9k-image.png)

Choose the Generate intermediate CSR option, and fill in the blanks.

Set Type to Internal.

Set your Common name to your domain with a suffix that it’s an Intermediate CA, like this:

```
ogsofttech.lan Int CA 01
```

Click Generate, to make the CSR.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/8u6Ai3jPsxRNzqew-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/8u6Ai3jPsxRNzqew-image.png)

Paste the CSR onto the clipboard, and save it as a file: `pki_intermediate.csr`.

Open the web UI for your root CA, and navigate to Secrets/PKI/Issuers, and click Sign Intermediate.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/T8DwcOLp0OuEd908-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/T8DwcOLp0OuEd908-image.png)

Paste the CSR content into the CSR field.

Set the domain name as the common name: ogsofttech.lan

Select pem\_bundle from the Format dropdown:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/bsh8zbtSHeyd0f7D-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/bsh8zbtSHeyd0f7D-image.png)

Click Sign, to sign the CSR from the Intermediate CA.

Once signed, you’ll see this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/A0CNNYtP5Y9IuvEO-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/A0CNNYtP5Y9IuvEO-image.png)

It contains the signed certificate for the Intermediate CA, the cert of the issuing CA, and the CA chain cert.

<p class="callout info">NOTE: Save all three in a known place, as this is the only time these will be available.</p>

Download the certificate as: `ogsofttech.lan-intca01.cert.pem`

Open the UI for the Intermediate CA, and navigate to the Import a CA:

<p class="callout info">NOTE: You get to this, by clicking on the Issuers tab, and clicking Import.</p>

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/nQN8tPwbVtk9I5Aa-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/nQN8tPwbVtk9I5Aa-image.png)

Upload the certificate that you saved to: `ogsofttech.lan-intca01.cert.pem`

Click Import Issuer.

The Issuers tab will show two entries, one for the root, and one for the Int CA:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/Xs71GkhQcFN32fe0-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/Xs71GkhQcFN32fe0-image.png)

## Issuing Role

Before you can issue certificates to hosts and such, you need to create an issuer role.

To do this, we need to identify the issuer’s Guid.  
We can get this by clicking the terminal button, and running this:

```bash
read -field=default pki/config/issuers
```

Note the Guid that was returned.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/RH1lhiMNltBT4skL-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/RH1lhiMNltBT4skL-image.png)

Now, go to Secrets/PKI/Roles, and create a new role.

Give the role a name in this format: ogsofttech-dot-lan

Unclick the Use Default Issuer, and select the Guid that matches what we found above.

Set the TTL to 43800 hours.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/lcE0BNnU8PL1PnT6-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/lcE0BNnU8PL1PnT6-image.png)

At the bottom of the form…

In the Allowed Domains field, add our domain: ogsofttech.lan.

Check the “Allow Subdomains” checkbox.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/DV4mRbtOBmSWzvHl-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/DV4mRbtOBmSWzvHl-image.png)

And, click Create, to make our issuing role.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/UiQVCPuZvmYtRzHP-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/UiQVCPuZvmYtRzHP-image.png)

Now, you have a role that can create and sign certificates.

If not already done, you can offline your root CA, as it needs to be kept safe, and only needed to create a new Intermediate CA.

See this page for how to generate certificates: [Generate Certificates with Hashicorp Vault](https://wiki.galaxydump.com/link/433)

# Handling Vault Node Restart

Each time a Vault node restarts, you will have to unseal it, so that it can participate in the cluster.  
Here are steps to do that.

Switch to root on the node with:

```bash
sudo -i
```

Set exports for the node:

<p class="callout info">NOTE: Make sure that the vault\_addr variable is pointing to the local node being unsealed, here.</p>

```bash
export VAULT_ADDR="https://vault0205.ogsofttech.lan:8200";
export VAULT_CACERT="/opt/vault/tls/ca.crt"
```

<p class="callout info">NOTE: Use the fully-qualified hostname above, as it appears in the node's cert.</p>

Now, unseal each node, by calling this command once each, for three of the five unseal keys:

<p class="callout info">NOTE: It will prompt you for the unseal key, each time you run it.</p>

```bash
vault operator unseal
```

Once unsealed, you can verify cluster membership with this:

```bash
vault operator raft list-peers
```

When run, you will see something like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/HCX5VSa7OOY5gn5s-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/HCX5VSa7OOY5gn5s-image.png)

If healthy, you will see one node as leader, and the others as voting followers.

<p class="callout info">NOTE: Make sure each node you configured, is present.</p>

# Local GPS NTP Time Server

Accessible at: 192.168.1.12  
Project files stored here: “\\\\192.168.1.11\\zfs\_mirror3\\Projects\\Dormant\\NTP Raspberry Pi Server”

## Build Data

Runs Raspian on a Raspberry PI

Static IP Address was set in: /etc/dhcpcd.conf

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/t3CNG2dvgpGka2lO-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/t3CNG2dvgpGka2lO-image.png)

Installed ntpstat with this:

```bash
sudo apt install ntpstat
```

## Usage

Here's some descriptions of the different tools for administrating NTP.

#### NTPSTAT

Use ntpstat to check if the NTP service is in sync:

```bash
ntpstat
```

It will return something like this:

```
synchronised to NTP server (149.20.54.20) at stratum 3 
   time correct to within 42 ms
   polling server every 1024 s
```

For our local NTP server (with GPS receiver), we see this:

```bash
pi@raspberrypi:~ $ ntpstat
synchronised to UHF radio at stratum 1
   time correct to within 2 ms
   polling server every 64 s
```

The ntpstat utility also returns an exit status, as a quick tell of synchronization.

Use this, following a call to ntpstat:

```
echo $?
```

The return value will mean one of these:

- If exit status 0 – Clock is synchronised.
- exit status 1 – Clock is not synchronised.
- exit status 2 – If clock state is indeterminant, for example if ntpd is not contactable.

#### NTPQ

Use ntpq -p to see the following:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/AgQlX6XhmF1rhxwg-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/AgQlX6XhmF1rhxwg-image.png)

#### PPTSTEST

Use this to test the pps signal:  
sudo ppstest /dev/pps0

The output should spit out a new line every second that looks something like this (your output will be a bit farther from x.000000 since it isn’t yet using the GPS PPS):

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/wReLzudcfng5LRCc-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/wReLzudcfng5LRCc-image.png)

#### GPSMON

Use the gps monitor function to check the realtime availability of GPS and satelite count and position, with this:

```bash
gpsmon
```

This will present the following:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/VSVAiDmLXiPfdwjq-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/VSVAiDmLXiPfdwjq-image.png)

#### CGPS

Or, using the following for a simpler gps status check:

```bash
cgps
```

This will create the following output:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/gcG0wI1LloRyuDuP-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/gcG0wI1LloRyuDuP-image.png)

### References

How to troubleshoot an NTP server: [https://support.ntp.org/Support/TroubleshootingNTP](https://support.ntp.org/Support/TroubleshootingNTP)

The following link is probably not what was used to build the house time server. But, this is an interesting reference on how to build a Raspberry pi GPS time server:  
<span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://blog.thelifeofkenneth.com/2020/03/building-raspberry-pi-stratum-1-ntp.html" data-inline-card="true" data-renderer-start-pos="934" data-ssr-placeholder="0vDZ-:EfLS5:4y5Pz:qz-Pe:F4Zdx-0"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Building a Raspberry Pi Stratum 1 NTP Server</span></span>](https://blog.thelifeofkenneth.com/2020/03/building-raspberry-pi-stratum-1-ntp.html)</span></span></span>

Here’s a good reference on how to tune and configure the NTP setup on a RaspBerry PI: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="http://www.satsignal.eu/ntp/Raspberry-Pi-NTP.html" data-inline-card="true" data-renderer-start-pos="1025" data-ssr-placeholder="0vDZ-:EfLS5:4y5Pz:qz-Pe:F4Zdx-1"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Building a Raspberry-Pi Stratum-1 NTP Server</span></span>](http://www.satsignal.eu/ntp/Raspberry-Pi-NTP.html)</span></span></span>

Another reference: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="https://austinsnerdythings.com/2021/09/29/millisecond-accurate-chrony-ntp-with-a-usb-gps-for-12-usd/" data-inline-card="true" data-renderer-start-pos="1049" data-ssr-placeholder="0vDZ-:EfLS5:4y5Pz:qz-Pe:F4Zdx-2"><span class="loader-wrapper"><span data-testid="hover-card-trigger-wrapper">[<span class="_19itglyw _vchhusvi _r06hglyw _o5721jtm _1nmz9jpi _16d9qvcn _ca0qv77o _u5f31b66 _n3tdv77o _19bv1b66" data-testid="inline-card-icon-and-title"><span class="_19itglyw _vchhusvi _r06hglyw">Millisecond accurate Chrony NTP with a USB GPS for $12 USD - Austin's Nerdy Things</span></span>](https://austinsnerdythings.com/2021/09/29/millisecond-accurate-chrony-ntp-with-a-usb-gps-for-12-usd/)</span></span></span>

Good reference on the Shared Memory Driver, and how the 127.127.28.x address is used: <span data-annotation-inline-node="true" data-annotation-mark="true" data-card-url="http://doc.ntp.org/archives/drivers/driver28/" data-inline-card="true" data-renderer-start-pos="1140" data-ssr-placeholder="0vDZ-:EfLS5:4y5Pz:qz-Pe:F4Zdx-3">[http://doc.ntp.org/archives/drivers/driver28/](http://doc.ntp.org/archives/drivers/driver28/)</span>

# Ubuntu: Use Private NTP Server

<p class="callout info">NOTE: If you are configuring Debian, see this page: [Debian 13: Use Private NTP Server](https://wiki.galaxydump.com/link/552)</p>

### Status

For hosts in isolated VLANS, we provide access to a local NTP server, via firewall rules.

The local NTP server is at: 192.168.1.12.

See this page for the local NTP server: [Local GPS NTP Time Server](https://wiki.galaxydump.com/link/456)

The main router includes a floating firewall rule that forwards UDP port 123 requests to the local NTP server.  
It includes most of the VLANS. But can be updated to include other VLANs.

### Configuration

Here are steps to configure an isolated Ubuntu VM to use the local NTP server.

Install NTP on the VM with:

```bash
sudo apt update
sudo apt install ntp
```

Edit the ntp config file to listen to the local NTP server.  
Open it with:

```bash
sudo nano /etc/ntpsec/ntp.conf
```

<p class="callout info">NOTE: NTP is handled by ntpsec in recent Ubuntu versions.  
And, its configuration is stored in: /etc/ntpsec.</p>

Locate the NTP pool entries, and comment out each one.

Then, add the local NTP server entry line:

```
server 192.168.1.12 iburst
```

The section should look like this:

```bash
# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See https://www.pool.ntp.org/join.html for
# more information.
server 192.168.1.12 iburst
#pool 0.ubuntu.pool.ntp.org iburst
#pool 1.ubuntu.pool.ntp.org iburst
#pool 2.ubuntu.pool.ntp.org iburst
#pool 3.ubuntu.pool.ntp.org iburst

# Use Ubuntu's ntp server as a fallback.
#server ntp.ubuntu.com
```

Restart the ntpsec service with:

```bash
sudo systemctl restart ntpsec
```

You can check the service status with this:

```bash
sudo systemctl status ntpsec
```

And, you can see the NTP status, with this:

```
ntpq -p
```

# Debian 13: Use Private NTP Server

### Status

For hosts in isolated VLANS, we provide access to a local NTP server, via firewall rules.

The local NTP server is at: 192.168.1.12.

See this page for the local NTP server: [Local GPS NTP Time Server](https://wiki.galaxydump.com/link/456)

The main router includes a floating firewall rule that forwards UDP port 123 requests to the local NTP server.  
It includes most of the VLANS. But can be updated to include other VLANs.

### Configuration

Here are steps to configure an isolated Debian 13 VM to use the local NTP server.

<p class="callout info">NOTE: Debian 13 uses a service called, timesyncd.  
Timesyncd is not as precise as true NTP.  
But, it is good enough.</p>

Timesyncd is installed by default.  
We will use it.

You can verify timesyncd is running with this:

```bash
timedatectl show-timesync --all
```

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/SsHARVIk8L0aUT0v-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/SsHARVIk8L0aUT0v-image.png)

Edit the config file to listen to the local NTP server.  
Open it with:

```bash
sudo nano /etc/systemd/timesyncd.conf
```

It will be mostly commented out, by default.  
This is because the service is using config defaults, or compiled defaults.

Update it to point to the local ntp server, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/niKhbMWl4lYwhx5g-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/niKhbMWl4lYwhx5g-image.png)

Close and save the config.

Once saved, restart the timesyncd service with:

```bash
sudo systemctl restart systemd-timesyncd
```

You can check the service status with this:

```bash
timedatectl show-timesync --all
```

# NGINX: Deploy SSL Certificate

Here's quick instructions for deploying an SSL key/cert pair to an NGINX instance.

<p class="callout info">NOTE: These steps are assumed to be executed as root.</p>

#### Elevate to Root

Elevate to root with this:

```bash
sudo -i
```

#### Create SSL Folder

By default, a fresh NGINX install doesn't yet contain a folder for certificates.

Create the ssl folder, with this:

```bash
mkdir /etc/nginx/ssl
```

#### Set Folder Permissions

We need to constrain access to the private SSL keys, with the following:

<p class="callout info">NOTE: NGINX runs as root, so this is fine.</p>

```bash
chmod 0600 -R /etc/nginx/ssl
chown root:root -R /etc/nginx/ssl
```

#### Copy SSL Cert

Create files in the SSL folder for the public crt and private key files, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/scaled-1680-/HqYGhtmKzVQAnf7X-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-09/HqYGhtmKzVQAnf7X-image.png)

Paste the content of your private key and public cert into the files.

#### Set File Permissions

Once added, constrain access, with the following:

```bash
# All certs readable
chmod 644 *.crt

# All keys locked down
chmod 600 *.key

# Make sure ownership is correct
chown root:root *.crt *.key
```

#### Restart NGINX

Once certs are pasted in, you need to restart NGINX for the new certs to take effect, with this:

```bash
nginx -s reload
```

# Windows File Explorer - Spawn New Window

Recent versions of Windows OS changed the behavior of how the Start Menu spawns applications.

Specifically: If an app is already open, clicking its Start Menu entry will pull the open app to foreground, instead of opening a new window.

For things like File Explorer, this can be annoying, if you intended to open a new window.

And, this is especially annoying when working in RDP sessions, where you cannot use the Win-E to force a new explorer window.

### The Fix for File Explorer

To fix this for File Explorer, requires a registry update.

#### For Current User

To fix this behavior for the current user, you can perform a registry key update, by downloading and clicking on the following file:

[FileExplorer\_NewWindow- CurrentUser.reg](https://wiki.galaxydump.com/attachments/5)

Your user will get the updated functionality, once you log out and back in.

#### For Default User

To make the fix for any new users, you can perform a registry key update, by downloading and clicking on the following file:

[FileExplorer\_NewWindow- Default.reg](https://wiki.galaxydump.com/attachments/8)

Any new users that logs into the machine will get the updated functionality.

<p class="callout info">NOTE: This doesn't affect existing users.</p>

#### For All Users

To fix the issue for all users (current and new), you can run the following powershell.

We've created a wrapper cmd file that opens and runs the powershell for you, to make it easier.

To run it, download both of these files:

[Fix-FileExplorer\_SeparateWindow.cmd](https://wiki.galaxydump.com/attachments/7)

[Set-ExplorerSeparateProcess-AllUsers.ps1](https://wiki.galaxydump.com/attachments/6)

Once downloaded, open a command line window with Administrative privileges.

Then, navigate to the folder where you downloaded the above files, and run the cmd file: Fix-FileExplorer\_SeparateWindow.cmd

It will open a powershell window, and update registry keys for the default and all existing users.

<p class="callout info">NOTE: Any existing users will need to log out and back in, to get the updated functionality.</p>

# Royal TS: Using SSH Keys

This is a workaround for using SSH keys in Royal TS.

#### Background

Royal TS does not provide an easy means to specify the username for an SSH key connection.

So, when connecting to a linux host with an SSH key, the host will prompt you for the username.

This is because, Linux does not have a direct association of a presented key to a user.  
And, an SSH key does not contain a username.

So, the username must be presented by the connecting client.

The problem is:  
Royal TS seems like it allows you to specify a private key and a username.  
And, the documentation says that you can specify it as such.  
But, if the username field is populated, Royal TS will attempt to connection with a password, and ignore the SSH key.

To solve this, you must do the following two things:

#### Set The Username

You have to specify the Computer name in this format:

```
username@hostname
```

Like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/IyaDRGLqykPNgzjh-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/IyaDRGLqykPNgzjh-image.png)

#### Connection Window Title Changes

But once you do that, Royal TS has another problem.

It will create a connection using your SSH key and the specified username.

BUT. The spawned connection becomes an external Putty window, that Royal TS can't embed.

What's happening is, the spawned Putty window (for your connection) gets a title that is different than what Royal TS expects.  
And, it won't embed the window, like other connections.

To fix this secondary problem, you must open the Advanced tab of the connection properties, and find the Features tab.

Then, check the box for: Disable Remote-Controlled Window Title Change.

Like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/pdu4Z3o4iycVPG6b-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/pdu4Z3o4iycVPG6b-image.png)

  
Once you check that box, the spawned Putty connection window will properly embed into the Royal TS UI.

### Alternate Method

An alternate method to this, is to migrate the connection to use the built-in SSH client, instead of Putty.

You can do this, by opening the connection properties, and finding the Active Plugin page.

Then, changing the Active Plugin to: Terminal (based on Rebex).

You will have to close and reopen the connection, after.

As well. It may give you an error, if you have included the username in the connection hostname field.

# Debian VM Template

#### Purpose

These instructions are for standing up a Debian 13.4 VM to be used as a template in an isolated manufacturing network.

It will be prepopulated with packages we anticipate needing:

- NGINX
- SubVersion
- net-tools
- Docker
- nano
- python
- sqlite

<p class="callout warning">NOTE: This process creates a template VM that is scrubbed of any machine-ID, host SSH keys, etc.  
These values are created each time the template is started. And, must be reset, for the VM to be a viable template, again.  
  
So. If you do make changes to the template VM after creation, you will have to rerun the cleanup and template reset scripts.  
  
To do so, follow the steps near bottom of this page, starting in the Cleanup section.</p>

#### Setup

Update Host

```bash
apt update
```

Install sudo:

```
apt install sudo
```

Add a user:

```bash
usermod -aG sudo yourusername
```

Add user to sudoers:

As root, open /etc/sudoers.

Add this entry to the bottom:

```bash
yourusername ALL=(ALL:ALL) ALL
```

#### Package Install

Update the base image:

```bash
sudo apt update
sudo apt upgrade -y
```

Install core admin and networking tools:

```bash
sudo apt install -y \
  openssh-server sudo ca-certificates curl wget gnupg lsb-release \
  nano vim less bash-completion locales tzdata \
  iproute2 net-tools dnsutils iputils-ping traceroute tcpdump nmap netcat-openbsd \
  htop iotop lsof psmisc strace procps sysstat \
  jq tree file unzip zip tar rsync dos2unix \
  apache2-utils openssl \
  chrony \
  git make build-essential python3 python3-pip python3-venv \
  nfs-common cifs-utils \
  parted gdisk smartmontools acl \
  tmux screen ncdu \
  sqlite3
```

Install network tools:

```bash
sudo apt install -y mtr-tiny socat
```

Install certificate debugging tools:

```bash
sudo apt install -y ssl-cert
```

Install http API debugging tools:

```bash
sudo apt install -y httpie
```

Install process debugging tools:

```bash
sudo apt install -y dstat
```

#### Install NGINX

```bash
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
```

#### Install Docker  


First remove conflicting packages:

```bash
sudo apt remove -y docker.io docker-doc docker-compose podman-docker containerd runc || true
```

Add Docker repo and key:

```bash
sudo install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/debian/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

Install Docker:

```bash
sudo apt update
sudo apt install -y \
  docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin
```

Enable Docker:

```bash
sudo systemctl enable docker
sudo systemctl start docker
```

Allow user to run Docker without sudo:

```bash
sudo usermod -aG docker $USER
```

Add offline package management for Docker:

```bash
sudo apt install -y dpkg-dev
```

#### SubVersion Client

Installing the SVN client is straightforward.

NOTE: It was actually installed as offline packages, after the VM was moved to the target network.  
The instructions, here, are to describe how it would have been installed, before the move.

See this page for details of how it was installed as an offline package: [Debian: Offline Package Installation](https://wiki.galaxydump.com/link/572)

To install the subversion client, use this:

```bash
sudo apt update
sudo apt install subversion
```

Once installed, you can confirm it with this:

```bash
svn --version
```

If successful it will display the help file with client version data.

#### .NET Runtime

This requires a couple steps.

Do this, first:

```bash
wget https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb -O packages-microsoft-prod.deb

sudo dpkg -i packages-microsoft-prod.deb

rm packages-microsoft-prod.deb

sudo apt update
```

Install the runtime:

```bash
sudo apt install -y dotnet-runtime-8.0
```

Install the aspnet runtime:

```bash
sudo apt install -y aspnetcore-runtime-8.0
```

Install the .NET sdk:

```bash
sudo apt install -y dotnet-sdk-8.0
```

Verify the installed version:

```bash
dotnet --info
```

Have it list the runtimes, as well:

```bash
dotnet --list-runtimes
```

Do a quick hello world, to test the runtime.

```bash
mkdir ./dotnet-test && cd ./dotnet-test

dotnet new console
dotnet run
```

#### Validation Checks

```bash
systemctl status nginx
systemctl status docker
docker run hello-world
```

#### VM Templating Setup

Follow the instructions on this page, to modify the VM for templating:

Doing so, allows the VM to deploy, without any chance of colliding machine-Id, host SSH keys, and such.

#### Cleanup  


Once the above is done, and you've followed the VM templating setup steps, you can do any final cleanup, with this:

<p class="callout info">NOTE: This step is included as a means to reduce the VM size for copying it across networks.  
You may not have to accomplish this each time you update the template VM.  
You can probably skip this step, if just updating the template VM, where it sits.</p>

```bash
sudo apt autoremove --purge -y
sudo apt clean
sudo rm -rf /var/lib/apt/lists/*
sudo journalctl --vacuum-time=1d
sudo rm -rf /tmp/* /var/tmp/*
```

#### Template Reset Script

Once the VM image size is reduced, you are ready to scrub it as a template.

Previous steps installed a template reset script, here:

```bash
/etc/template/reset.sh
```

Run it each time you shutdown the template VM, to reset needed info, for cloning.

<p class="callout info">NOTE: This is what you will execute, each time you had to start up the template VM, to make modifications to it.  
Follow this command with a PowerOff command. See PowerOff section.</p>

It can be executed with this:

```bash
sudo /etc/template/reset.sh
```

The script contents are here: [Template VM Reset Script](https://wiki.galaxydump.com/link/391#bkmrk-and%2C-populate-it-wit)

#### Zero Out Free Space

To make the VM smaller, this needs to be done.

<p class="callout info">NOTE: This step is included as a means to reduce the VM size for copying it across networks.  
You may not have to accomplish this each time you update the template VM.  
You can probably skip this step, if just updating the template VM, where it sits.</p>

```bash
sudo dd if=/dev/zero of=/EMPTY bs=1M status=progress || true
sudo rm /EMPTY
sync
```

#### Power Off

Instead of a shutdown, you should execute a power off with this:

```bash
sudo poweroff
```

### VM Export

The easiest way to move the VM to another datacenter, is by turning it into an OVA.

Follow the instructions on this page, to convert it to an OVA: [VMWare Export OVA](https://wiki.galaxydump.com/link/559)

### Further Packages

You will inevitably need to install packages, after the VM has been deployed inside the isolated network.

See this page for details of how we installed the SubVersion client as an offline package: [Debian: Offline Package Installation](https://wiki.galaxydump.com/link/572)

<p class="callout info">NOTE: Each time you make changes to the template VM, that requires starting it up, which creates fresh machine-id host SSH keys, etc.  
So, you have to clear those out, before the VM is a viable template, again.  
Start in the Cleanup section, above, and follow steps, to make the VM a viable template, again.</p>

# VMWare Export OVA

Here are instructions for how to export an OVA from a VSphere host.

Locate your credentials for the ESXi host.

Identify the inventory name of the VM that you want to export.

Download the VMWare-ovftool.

A copy is here: "\\\\192.168.1.211\\Programs\\VMWare\\OVF Tool\\VMware-ovftool-4.4.3-18663434-win.x86\_64"

Open a command line window, and use the following:

```
ovftool.exe --noSSLVerify vi://<esxhostname>/<vmname> "D:\<vmname>.ova"
```

Replace the &lt;esxhostname&gt; with the name or IP address of your ESXi host.

Replace the &lt;vmname&gt; with the inventory name of the vm to be exported as OVA.

It will ask you for login credentials, and execute the export to the target folder.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/KbNyUXCsoVZxr2uQ-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/KbNyUXCsoVZxr2uQ-image.png)

Once done, the OVA will be created in the target folder you specified.

It can be moved to the new environment and imported.

# Debian: Offline Package Installation

If you need to add services or packages to a VM that has no internet access to pull down packages, here's an offline method.

NOTE: These steps were performed and vetted when updating a template that was already deployed inside an isolated network (without internet access). See this page: [Debian VM Template](https://wiki.galaxydump.com/link/558)

#### Needed Elements

For this method, we need a few pieces to get the job done.

- Sudo access on the target VM (the one without internet access).
- Access to a surrogate VM that has internet visibility

<p class="callout info">NOTE: In order to correctly retrieve packages that an offline VM requires, the surrogate VM needs to be the same Distro and very close to the same OS version as the target VM.  
This ensures that you fetch the correct secondary dependencies (of the target package) that are needed (by the same Distro/version).  
</p>

- The ability to ferry a set of package files across the network boundary to the isolated, target VM.

#### Process Steps

For demonstrations purposes, we will attempt to perform an offline install of SubVersion on a Debian 13.4 VM, using a surrogate Debian 13.5 VM with internet access.

#### Surrogate VM

Log into the surrogate VM.

From an SSH session, create a download folder where you will download packages, and navigate to it.

```bash
mkdir -p ~/Desktop/temppkg/subversion
cd ~/Desktop/temppkg/subversion
```

Once inside the download folder, we will download the desired package and secondary dependencies.\\

#### Apt-RDepends

<p class="callout info">NOTE: Since any package that you want to install, has dependencies of its own, we will be downloading these secondary dependencies, as well.</p>

<p class="callout info">NOTE: Downloading dependency sets, requires apt-rdepends, which is not part of a normal OS build.  
This next step will install it.  
If you skip it, you will get an error message, like this:  
Command 'apt-rdepends' not found, but can be installed with: sudo apt install apt-rdepends  
</p>

Before downloading the offline dependency set, you will need to ensure that apt-rdepends is installed on the surrogate VM.  
Install apt-rdepends, with this:

```bash
sudo apt update
sudo apt install -y apt-rdepends
```

With apt-rdepends installed, we can safely download the target package and its secondary dependencies.

#### Package Download

The following command will download the subversion package and all of its dependencies needed for a Debian 13.4 OS.

```bash
apt-get download $(apt-rdepends subversion | grep -v "^ ")
```

<p class="callout info">NOTE: You can change the package name, in the above command, to get the dependency set for any package.</p>

Here's what the downloaded dependency set (for subversion) looks like, today:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/xensHjdEP74BjoOU-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/xensHjdEP74BjoOU-image.png)

#### Target VM

Next, you need to move the dependency set over to the target VM, by whatever means you have.  
For me, I copied them out of the surrogate, via SCP.  
Then, they were ferried across the network boundary as committed changes to an SVN repository (visible on both sides).

Next, log into the target VM, and create a download folder, where you will push the dependency set.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/EQoTcbN4RGKkJNQf-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/EQoTcbN4RGKkJNQf-image.png)

Then, another SCP push to a temporary folder on the target VM.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/UGEfnkLecfQwbJcJ-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/UGEfnkLecfQwbJcJ-image.png)

Once the packages are on the target VM, open an SSH session to it, and navigate to the download folder.

Confirm the dependency set is there:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/UpfJQ2ey2Fmzbgnf-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/UpfJQ2ey2Fmzbgnf-image.png)

<p class="callout warning">Warning: To prevent an apt elevation warning, read ahead on how to set permissions on the dependency folder, first.</p>

From inside the folder with the dependencies, run this, to install all of them:

```bash
sudo apt install ./*.deb
```

The installation ran for a good bit, and finished.  
But, it did have, what appears to be, an error.

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/2S1jcWa0v3VdRaVK-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/2S1jcWa0v3VdRaVK-image.png)

At the tail of the installation scroll, you may see an `_apt` notice that looks like an error. See above.  
That final notice is not a failure.  
It just means APT could not read the local `.deb` files as the low-privilege `_apt` user, so it read them as root instead.

That notice can be prevented by making the folder/files world-readable before installing.  
Run this to make the downloaded packages and folder world-readable:

```bash
chmod -R a+rX ~/Desktop/temppkg
```

#### Validation

Since we were installing SubVersion, we can confirm its installation, with this:

```bash
svn --version
```

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/scaled-1680-/jYQLZsXBBO1FLYEn-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-04/jYQLZsXBBO1FLYEn-image.png)

#### Cleanup

Once installed, you can remove the packages and download folder, with this:

```bash
cd ~/Desktop
rm -rf ~/Desktop/temppkg
```

# Debian: Setup Static IP Address

<p class="callout info">NOTE: This works on Debian 13.4.</p>

Here are notes and steps to assign a static IP address for a Debian host.

<p class="callout info">NOTE: If you are doing this for an Ubuntu host, see this page: [Ubuntu: Setup Static IP Address](https://wiki.galaxydump.com/link/119)</p>

#### Net Tools (ipconfig)<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Install net tools, so we can use commands like: ipconfig

`sudo apt install net-tools`

#### Static IP Address<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Static addresses will be defined in a netplan file, configured below.

But, we need to do a few things, first:

- Get the Gateway IP
- Enable Adapters

#### Get the Gateway IP

You will need to know the gateway address that your host will use.

If the host is not on the desired network, you will need to determine the gateway IP, manually.  
Or, you can join the host to the network, and do the following to determine it.

If the host is up, you can run this command to get the current default gateway assigned to it:

`ip r | grep default`

This command will return the default gateway address, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/scaled-1680-/h6MJkA6IxvRSQ4ii-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/h6MJkA6IxvRSQ4ii-image.png)

#### Enable Adapters<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

If you added an adapter to the host, it may be in a down state.

To enable it, use this command to find the name:

```bash
sudo ip a | grep ^[[:digit:]]
```

The above will give a list, like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/scaled-1680-/uVSzS5rVVjzGu6Oi-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2025-02/uVSzS5rVVjzGu6Oi-image.png)

And, you can enable it with this:

```bash
sudo ifconfig eth1 up
```

<p class="callout info">NOTE: Be sure to use the name of the nic, from the previous call.</p>

#### Network Interfaces<button aria-hidden="true" class="cc-1r0b9w7" data-testid="anchor-button" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

Open the interfaces file at: /etc/network/interfaces

```bash
sudo nano /etc/network/interfaces
```

```bash
auto ens192
iface ens192 inet static
    address 192.168.1.50/24
    gateway 192.168.1.1
    dns-nameservers 192.168.1.2 8.8.8.8
```

Replace:

- `ens192` with your actual interface name (found above)
- `192.168.1.50` with your desired static IP
- `192.168.1.1` with your gateway/router
- Set the dns-nameservers you need with a space between each.

Save and close the interfaces file.

Restart Networking

With the config updated, we need to restart networking.

Do this:

```bash
sudo systemctl restart networking
```

Now, check the IP address with this:

```bash
sudo ifconfig
```

You'll see something like this:

[![image.png](https://wiki.galaxydump.com/uploads/images/gallery/2026-05/scaled-1680-/vLzzNb9VY1MYjKRW-image.png)](https://wiki.galaxydump.com/uploads/images/gallery/2026-05/vLzzNb9VY1MYjKRW-image.png)

# Windows 10 Moving Recovery Partition

If you've tried to expand a disk, but the Recovery Volume is in the way, here's how to move it.

Cached from here: [https://thedxt.ca/2023/06/moving-windows-recovery-partition-correctly/](https://thedxt.ca/2023/06/moving-windows-recovery-partition-correctly/)

<header class="entry-header" id="bkmrk-moving-windows-recov"># Moving Windows Recovery Partition Correctly

##### <span class="post-date"> [<time class="entry-date" datetime="2023-06-10T09:26:46-06:00">June 10, 2023</time>](https://thedxt.ca/2023/06/moving-windows-recovery-partition-correctly/ "9:26 am")</span>

</header>Recently I needed to expand a disk on a Windows 10 VM and a Windows Server 2022 VM, but I couldn’t because the Recovery Partition was in the way.

When searching for a way to do this I discovered that the internet is full of posts about simply deleting the Windows Recovery Partition. I am not a fan of simply deleting a recovery tool. On numerous occasions the recovery partition has been instrumental in helping me to fix a system.

If you search for how to move the Windows Recovery Partition the internet has many posts of fake ways to do it or ways to do it with third-party tools like GParted. I have nothing against third-party tools or GParted and I don’t doubt some of those methods do work. The issue I have with those methods is that you have to take the system offline in order to do them or the tools cost money.

Now yes you could just delete the Windows Recovery partition, but before you do that make sure you understand that you will lose a bunch of recovery options. You can read more about the recovery options you’ll lose in an earlier post I made about the [Windows Recovery Partition](https://thedxt.ca/2022/06/windows-recovery-partition/).

Here’s how to correctly move the Windows Recovery Partition on a Windows server or a normal Windows system.

This is what my partitions look like in Disk Management.

<div class="entry-content" id="bkmrk-"><div class="wp-block-image"><figure class="aligncenter size-full">[![](https://thedxt.ca/wp-content/uploads/2023/06/diskmgmt-before.png)](https://thedxt.ca/wp-content/uploads/2023/06/diskmgmt-before.png)</figure></div></div>We will move the 1 GB recovery partition to the end of the disk allowing us to add the 50 GB of unallocated space to the C drive.

# The Process

<div class="entry-content" id="bkmrk-make-sure-you-have-a">- Make sure you have a backup of the system you are going to edit the partitions on.
- Open Command Prompt as admin

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/CMD-Admin.png)<figcaption class="wp-element-caption">Run CMD as admin</figcaption></figure></div><span id="bkmrk--1"></span></div>## Disabling The Windows Recovery Partition

<div class="entry-content" id="bkmrk-we-need-to-disable-t">- We need to disable the existing Windows Recovery Partition to do that run the command `reagentc /disable`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/reagentc-disable.png)<figcaption class="wp-element-caption">Disabling the Recovery Partition</figcaption></figure></div></div>The `reagentc /disable` command will disable the recovery partition and will move the recovery partition into a file named `Winre.wim` and will be located in `C:\Windows\System32\Recovery` (you have to enable showing hidden system files if you want to see it)

<div class="entry-content" id="bkmrk-the-windows-recovery"><div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/disabled-windows-RE.png)<figcaption class="wp-element-caption">The Windows Recovery Partition File</figcaption></figure></div></div>## DiskPart

<div class="entry-content" id="bkmrk-run-the-command%C2%A0disk">- Run the command `diskpart` to launch DiskPart

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/diskpart.png)<figcaption class="wp-element-caption">Launching DiskPart</figcaption></figure></div>- List the disks in your system. You can do this by using the command `list disk`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/list-disk.png)<figcaption class="wp-element-caption">Listing the disks in DiskPart and showing the disk is a GPT disk</figcaption></figure></div></div>Pro tip from Matt in the comments, if there’s a \* in the column for Gpt that means the disk is likely a GPT disk and if there isn’t a \* in the Gpt column the disk is likely MBR. Make a note of this as it will be important further down.

<div class="entry-content" id="bkmrk-select-the-disk-you-">- Select the disk you need to move the recovery partition on. You can do this by using the command `select disk` and the disk number. In my setup disk 0 was the correct disk and the command I entered was `select disk 0`.

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/sel-disk.png)<figcaption class="wp-element-caption">Selecting the disk in DiskPart</figcaption></figure></div>- List the partitions on that disk. You can do this by using the command `list partition`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/list-par.png)<figcaption class="wp-element-caption">Listing the partitions in DiskPart</figcaption></figure></div>- Select the recovery partition. You can do this by using the command `select partition` and the partition number. In my setup partition 4 is my recovery partition and the command I entered was `select partition 4`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/sel-par.png)<figcaption class="wp-element-caption">Selecting the partition in DiskPart</figcaption></figure></div></div>The recovery partition is a protected partition so we need to use a bit more force to delete it.

<div class="entry-content" id="bkmrk-force-the-deletion-o">- Force the deletion of the recovery partition. You can do this by using the command `delete partition override`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/del-par-force.png)<figcaption class="wp-element-caption">Forcing the partition deletion</figcaption></figure></div></div>## Disk Management

Now if you look in Disk Management you should no longer have the Recovery Partition and it should show up as unallocated.

<div class="entry-content" id="bkmrk-disk-management-with"><div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/deleted-Windows-RE.png)<figcaption class="wp-element-caption">Disk Management with the Recovery Partition deleted</figcaption></figure></div>- Expand your disk and leave about 1024 MB off your resized size to leave room for the re-enabling the Recovery Partition.

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/Extend-Disk.png)<figcaption class="wp-element-caption">Expanding the partition but leaving room for the Windows Recovery Partition</figcaption></figure></div></div>Disk Management should now look something like this.

<div class="entry-content" id="bkmrk-disk-management-afte"><div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/resized-disk.png)<figcaption class="wp-element-caption">Disk Management after expanding the disk and leaving room for the Windows Recovery Partition</figcaption></figure></div></div>Once the disk is expanded we need to rebuild everything that is needed for Windows to know that the extra space that we left unallocated can be used to for the recovery partition.

<div class="entry-content" id="bkmrk-create-a-new-simple-">- Create a New Simple Volume with the unallocated space.

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/new-simple-vol.png)<figcaption class="wp-element-caption">Creating a New Simple Volume</figcaption></figure></div>- Don’t give it a drive letter.

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/no-dirve-letter.png)<figcaption class="wp-element-caption">Not giving the New Simple Volume a drive letter or a drive path</figcaption></figure></div>- You can give the new partition a name if you want it does not mater. I’m going to call mine New Recovery.

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/partition-name.png)<figcaption class="wp-element-caption">Naming the New Simple Volume</figcaption></figure></div></div>Disk Management should now look something like this.

<div class="entry-content" id="bkmrk-disk-management-with-1"><div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/diskmgmt-new-par.png)<figcaption class="wp-element-caption">Disk Management with the newly created partition that will become the Windows Recovery Partition</figcaption></figure></div></div>## Back to DiskPart

<div class="entry-content" id="bkmrk-in-diskpart-list-you">- In DiskPart list your partitions again by running the command `list partition`

<div class="wp-block-image"><figure class="aligncenter size-full is-resized">![](https://thedxt.ca/wp-content/uploads/2023/06/list-par-2nd-time.png)<figcaption class="wp-element-caption">Listing the partitions with DiskPart</figcaption></figure></div>- Select the 1024 MB partition with the command `select partition` and the partition number. In my setup it was partition 4 and the command I ran was `select partition 4`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/sel-par.png)<figcaption class="wp-element-caption">Selecting the partition with DiskPart</figcaption></figure></div></div>If you have a GPT disk you need to run some very specific command and if you have an MBR disk you need to run different very specific commands.

### GPT disk

On GPT disks we need to change the partition ID to **de94bba4-06d1-4d40-a16a-bfd50179d6ac** which tells Windows that this is a recovery partition

<div class="entry-content" id="bkmrk-run-the-following-co">- Run the following command to set the partition as a recovery partition `set id=de94bba4-06d1-4d40-a16a-bfd50179d6ac`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/GPT-par-ID.png)<figcaption class="wp-element-caption">Setting the GPT partition ID in DiskPart</figcaption></figure></div></div>We also need to hide the drive and flag it as a required partition to do that we have to set a GPT attribute to **0x8000000000000001**

<div class="entry-content" id="bkmrk-run-the-following-co-1">- Run the following command to set the GPT attribute to hide the drive and flag it as required `gpt attributes=0x8000000000000001`

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/gpt-attribute.png)<figcaption class="wp-element-caption">Setting the GPT attribute in DiskPart</figcaption></figure></div>- Now we can exist DiskPart.

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/exit-diskpart.png)<figcaption class="wp-element-caption">Exiting DiskPart</figcaption></figure></div></div>### MBR disk

On MBR disks we need to change partition ID to **27** which will tell Windows that this is a recovery partition.

<div class="entry-content" id="bkmrk-run-the-following-co-2">- Run the following command to set the partition as a recovery partition `set id=27`

<div class="wp-block-image"><figure class="aligncenter size-full is-resized">![](https://thedxt.ca/wp-content/uploads/2023/06/BIOS-par-ID.png)<figcaption class="wp-element-caption">setting the MBR partition ID in DiskPart</figcaption></figure></div>- Now we can exist DiskPart.

<div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/exit-diskpart.png)<figcaption class="wp-element-caption">Exiting DiskPart</figcaption></figure></div></div>## Enabling The Windows Recovery Partition

<div class="entry-content" id="bkmrk-now-we-can-re-enable">- Now we can re-enable the recovery partition by running the command `reagentc /enable`

<div class="wp-block-image"><figure class="aligncenter size-full is-resized">![](https://thedxt.ca/wp-content/uploads/2023/06/reagentc-enable.png)<figcaption class="wp-element-caption">Enabling the Windows Recovery Partition</figcaption></figure></div></div>The `reagentc /enable` command will copy the `Winre.wim` file from `C:\Windows\System32\Recovery` into our new recovery partition.

<div class="entry-content" id="bkmrk-windows-recovery-par"><div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/enabled-windows-RE.png)<figcaption class="wp-element-caption">Windows Recovery Partition file is now back on the recovery partition</figcaption></figure></div></div>If you look at Disk Management again everything shows up correctly.

<div class="entry-content" id="bkmrk--2"><div class="wp-block-image"><figure class="aligncenter size-full">![](https://thedxt.ca/wp-content/uploads/2023/06/moved-Windows-RE.png)</figure></div></div>That’s all there is to it.

Technically speaking we did just delete the Windows Recovery Partition but we did so in a way to keep our existing recovery partition safely intact and then we rebuild the recovery partition and re-enabled it.

I prefer doing it this way as it leaves your recovery options intact and you can do it all live without any reboots.

If you want to read more about deploying the Windows Recovery Partition you can do so by reading [Microsoft’s documentation about it](https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/deploy-windows-re).

If you want to read more about reagentc command you do so by reading [Microsoft’s documentation about it](https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/reagentc-command-line-options).