<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kirby Angell</title>
    <description>The latest articles on DEV Community by Kirby Angell (@kangell).</description>
    <link>https://dev.to/kangell</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1336340%2F2a50a547-d3ad-4b35-8bb1-dee6f0e32a6a.jpg</url>
      <title>DEV Community: Kirby Angell</title>
      <link>https://dev.to/kangell</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kangell"/>
    <language>en</language>
    <item>
      <title>VirtualBox: Using host's Ollama from guest VMs.</title>
      <dc:creator>Kirby Angell</dc:creator>
      <pubDate>Fri, 18 Apr 2025 17:00:28 +0000</pubDate>
      <link>https://dev.to/kangell/virtualbox-using-hosts-ollama-from-guest-vms-50ml</link>
      <guid>https://dev.to/kangell/virtualbox-using-hosts-ollama-from-guest-vms-50ml</guid>
      <description>&lt;p&gt;In this article we'll be looking at using locally hosted AI in a VirtualBox environment. At the end you'll have a VirtualBox Guest running VSCode/VSCodium with the Continue extension using the VirtualBox host's Ollama service. Why would you want to do this? I don't know, I'm not you, but it is a situation I needed to solve. I have a host machine running Windows, but the work I do is almost exclusively for Linux projects. Rather than dual boot I've opted to keep Windows as the host and setup VirtualBox guest virtual machines.&lt;/p&gt;

&lt;p&gt;I am assuming you already have VirtualBox installed and your Guest VM running VSCode and now want to install Ollama and access it from the guest. The host will be providing the Ollama service. I've tried in the past to have Ollama running in a guest and exposing the host GPU for it to use. It is possible to make this work, but doing so always leaves me feeling empty and unfulfilled due to what I perceive as the inefficiency in such a setup. Also its usually a pain to setup. This is easy-peasy so lets get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Ollama
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzo5hmvqz9ecuuycud9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzo5hmvqz9ecuuycud9r.png" alt="https://ollama.com/download/windows" width="388" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Windows installing Ollama couldn't be easier. You can download the installer here: &lt;a href="https://ollama.com/download/windows" rel="noopener noreferrer"&gt;https://ollama.com/download/windows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find &lt;em&gt;OllamaSetup.exe&lt;/em&gt; in your Downloads folder and run it. Click the &lt;strong&gt;Install&lt;/strong&gt; button and !boom! done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbk60m5iywi4pwbs8jae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbk60m5iywi4pwbs8jae.png" alt="click the install button" width="373" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The setup program not only installs Ollama, but starts the background service automatically. &lt;/p&gt;

&lt;p&gt;Next we need to install one or more models. There are tons of choices here. Which ones you use are largely user and task specific. You can find a list of the models Ollama supports and instructions to install them at &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;https://ollama.com/library&lt;/a&gt;. The models I am using, llama3 and qwen2.5-coder can be installed using these commands from a terminal window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama pull llama3.1:8b
ollama pull qwen2.5-coder:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two have very good performance on my system with an AMD Radeon RX 7700S with 8 gigabytes of VRAM. &lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing Ollama from the Guest VM
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzg670o7wz5hhokqrk27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzg670o7wz5hhokqrk27.png" alt="badly rendered diagram of the environment" width="741" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is our environment. Host running Ollama and Guest wanting to access it. The Guest networking is being provided by VirtualBox's "NAT" network. If you've left everything as default then as seen from the Guest, the Host's IP address is 10.0.2.2. The Ollama service runs on port 11434. So you can access it with the URL "&lt;a href="http://10.0.2.2:11434" rel="noopener noreferrer"&gt;http://10.0.2.2:11434&lt;/a&gt;" in the Guest just to make sure it is working:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadidqduflbo4vfvgmdne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadidqduflbo4vfvgmdne.png" alt="Image description" width="303" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we're inside the Guest VM lets take care of getting Continue added to VSCode. To do that click the &lt;em&gt;Extensions&lt;/em&gt; button on the left side of the VSCode window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uoersj1edtw35fwu2tb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uoersj1edtw35fwu2tb.png" alt="Click the button" width="372" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;Search&lt;/em&gt; field type "Continue" then click the install button for the extension. Once it is installed you're ready to configure it to use your local Ollama service. To do that you need to edit a text file inside the Guest VM. This is where the file is for Linux and Windows guests:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Linux: *~/.continue/config.yaml*
Windows: *%USERPROFILE%\\.continue\\config.yaml*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Regardless of the Guest OS here is what to put in that file, replacing the &lt;em&gt;Local Assistant&lt;/em&gt; section entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Local Assistant
version: 1.0.0
schema: v1
models:
  - name: chat
    provider: ollama
    model: llama3.1:8b
    apiBase: http://10.0.2.2:11434
    type: chat
  - name: autocomplete
    provider: ollama
    model: qwen2.5-coder:latest
    apiBase: http://10.0.2.2:11434
    type: autocomplete
context:
  - provider: code
  - provider: docs
  - provider: diff
  - provider: terminal
  - provider: problems
  - provider: folder
  - provider: codebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you save the file Continue will probably pick up the changes automatically. If it doesn't click on &lt;em&gt;Extensions&lt;/em&gt; again and see if Continue has a "reload" button displayed and click that if so. Otherwise if it still hasn't picked up the changes just restart VSCode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkbvxpmgxnueuaif1zqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkbvxpmgxnueuaif1zqf.png" alt="Image description" width="632" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything has gone well then you can highlight some code and press CTRL-L and Continue will open a pane to let you ask questions about the block of code. Above I'm asking it why my overly complex &lt;strong&gt;Hello World&lt;/strong&gt; program isn't working and Continue is pointing out my obvious mistake and offering to fix it. Fantastic.&lt;/p&gt;

&lt;p&gt;Incidentally you can use the same URL to expose the Host's Ollama service to other applications and services running in your Guest. For instance here is a Docker Compose file that will run the &lt;a href="https://www.openwebui.com/" rel="noopener noreferrer"&gt;Open WebUI&lt;/a&gt; service inside of your Guest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.8'
services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    ports:
      - "3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://10.0.2.2:11434
    volumes:
      - open-webui:/app/backend/data
    restart: always
    deploy:
      mode: replicated
      replicas: 1

volumes:
  open-webui:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have fun with your locally hosted AI coding assistant!&lt;/p&gt;

</description>
      <category>virtualbox</category>
      <category>ollama</category>
      <category>vscode</category>
      <category>ai</category>
    </item>
    <item>
      <title>Private Docker Registry</title>
      <dc:creator>Kirby Angell</dc:creator>
      <pubDate>Mon, 10 Feb 2025 20:18:19 +0000</pubDate>
      <link>https://dev.to/kangell/private-docker-registry-4o47</link>
      <guid>https://dev.to/kangell/private-docker-registry-4o47</guid>
      <description>&lt;p&gt;Recently, as of this post, Docker Hub has raised their rates for their "Teams" platform. We were mostly using Docker Hub to host private images and made little use of "Teams". I can't say if it was an unreasonable rate hike or not, but it was enough for me to look at our usage compared to the new price. We have private images to load into containers on our servers. The software to host a Docker registry, the service Docker Hub provides, is itself available as a Docker image. So we have our own servers, why not our own registry? &lt;/p&gt;

&lt;p&gt;I setup a registry for our company to use for its private images and here's how you can too. As a bonus I've included a simple web based UI you can use to examine what images are being hosted in your private registry.&lt;/p&gt;

&lt;p&gt;In this post I will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Docker compose file for our registry; and&lt;/li&gt;
&lt;li&gt;Configure the registry to proxy requests to Docker Hub for public images; and&lt;/li&gt;
&lt;li&gt;Create an NGINX configuration with SSL and password support to act as a proxy to our registry; and&lt;/li&gt;
&lt;li&gt;Demonstrate how to push and pull images from our private registry.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will need to know how to do the following tasks that are not covered in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create, edit and deploy Docker containers from a compose YML file; the compose file can also be used in Portainer and probably other Docker management tools; and&lt;/li&gt;
&lt;li&gt;Familiarity with NGINX configuration or some other proxy tool as it is not a good idea to directly expose your docker registry to the Internet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  compose.yml
&lt;/h2&gt;

&lt;p&gt;Here is the compose.yml file we'll use to spin up our registry and UI. I'll go over some configuration you'll want to do for your particular environment below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  registry-ui:
    image: joxit/docker-registry-ui:main
    restart: always
    ports:
      - 80:80
    environment:
      - SINGLE_REGISTRY=true
      - REGISTRY_TITLE=Docker Registry UI
      - DELETE_IMAGES=true
      - NGINX_PROXY_PASS_URL=http://registry-server:5000
      - SHOW_CONTENT_DIGEST=true
      - SHOW_CATALOG_NB_TAGS=true
      - CATALOG_MIN_BRANCHES=1
      - CATALOG_MAX_BRANCHES=1
      - TAGLIST_PAGE_SIZE=100
      - REGISTRY_SECURED=false
      - CATALOG_ELEMENTS_LIMIT=1000
    container_name: registry-ui

  registry-server:
    image: registry:2
    restart: always
    ports:
      - "5000:5000"     
    volumes:
      - /var/lib/docker-registry:/var/lib/registry
    environment:
      - REGISTRY_STORAGE_DELETE_ENABLED=true
      - REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io
      - REGISTRY_PROXY_USERNAME= 
      - REGISTRY_PROXY_PASSWORD= 
    container_name: registry-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  registry-ui
&lt;/h2&gt;

&lt;p&gt;The principal change you'll need to make to this service is the port you want the container to listen on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ports:
      - 80:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want the UI to be available on a different port change the port number on the left side of the colon. If you want it to listen on port 8080 then you would change the ports specification to "8080:80".&lt;/p&gt;

&lt;p&gt;You may also want to edit these options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      - REGISTRY_TITLE=Docker Registry UI
      - DELETE_IMAGES=true
      - NGINX_PROXY_PASS_URL=http://registry-server:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you can change the title of the web page displayed. I like to have an easy way to delete images so I have "DELETE_IMAGES" enabled, but you can set it to "false" if you don't want this feature. "NGINX_PROXY_PASS_URL" should be set to the name of your registry service ("registry-server" in my example) and the port it is listening on (5000). &lt;/p&gt;

&lt;p&gt;You can find more information on the UI &lt;a href="https://github.com/Joxit/docker-registry-ui" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  registry-server
&lt;/h2&gt;

&lt;p&gt;For the registry service, like the UI, you set the port you want the registry to listen on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   ports:
      - "5000:5000"     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    volumes:
      - /var/lib/docker-registry:/var/lib/registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could use a persistent Docker volume to store the registry images, but instead I map a directory manually. This is just personal preference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    environment:
      - REGISTRY_STORAGE_DELETE_ENABLED=true
      - REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io  
      - REGISTRY_PROXY_USERNAME= 
      - REGISTRY_PROXY_PASSWORD= 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first option turns on the ability to delete images from the registry. Simple enough, set it to "false" if you don't want your registry to allow this.&lt;/p&gt;

&lt;p&gt;The rest of this section is entirely optional. If included your private docker registry will use the public Docker Hub registry to attempt to load any images you ask for that aren't in your registry. Doing this allows you to use the same process to load images for your containers whether they are your private images or public images.&lt;/p&gt;

&lt;p&gt;You probably won't need to change "REGISTRY_PROXY_REMOTEURL"; it points to the Docker Hub registry as it is. &lt;/p&gt;

&lt;p&gt;Docker Hub does throttle requests:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxh831irpzfcagj0s7lr6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxh831irpzfcagj0s7lr6.png" alt="Throttling" width="673" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If 10 per hour isn't enough for your setup then you can create a free tier Docker Hub account and then configure your private registry to pull using those credentials. That will give you 40 pulls per hour from Docker Hub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      - REGISTRY_PROXY_USERNAME= 
      - REGISTRY_PROXY_PASSWORD= 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"REGISTRY_PROXY_USERNAME" must be set to the user name of your Docker Hub account. "REGISTRY_PROXY_PASSWORD" can be set to your Docker Hub password but I don't recommend that. Instead go to your Account Settings and click "Personal Access Tokens".&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feaxkj4mhdf3e7pnlky50.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feaxkj4mhdf3e7pnlky50.png" alt="Personal Access tokens" width="800" height="281"&gt;&lt;/a&gt;&lt;br&gt;
When you create a new token make sure you copy to the clipboard right then. You won't be able to see it later. Paste that token into the "REGISTRY_PROXY_PASSWORD" field.&lt;/p&gt;

&lt;p&gt;A benefit of having your private registry act as a proxy for the Docker Hub registry is that your private registry will cache those images. So if you have a lot of containers to spin up that rely on public images only 1 pull from Docker Hub will be made for the 1st container and all the rest will get the cached version.&lt;/p&gt;
&lt;h2&gt;
  
  
  Start the Registry
&lt;/h2&gt;

&lt;p&gt;You start the registry like you would any other docker compose project. From the directory where you have the &lt;em&gt;compose.yml&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Depending on which version of docker you are using the command
# is:
docker compose up 
# or
docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need to be a member of the "docker" group or use "sudo" to start the containers. &lt;/p&gt;

&lt;p&gt;If your containers start without errors, then you can CTRL-C to kill them and restart them in the background by adding "-d".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose up -d
# or
docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Proxy the Registry Using NGINX
&lt;/h2&gt;

&lt;p&gt;I recommend setting up NGINX or some other web proxy in front of your docker registry especially if you are already using it for other services you host. There are so many variations to NGINX configurations that I can't give you a stock, "this will work", for your setup. But this basic configuration works for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Upstreams
upstream docker-registry {
   server 127.0.0.1:5000;
}


server  {
  listen 10000 ssl;
  server_name    myserver.com;

  ssl_protocols TLSv1.3 TLSv1.2;

  chunked_transfer_encoding on;
  gzip off;

  client_max_body_size 0;  
  proxy_buffering off;  
  proxy_request_buffering off;  
  proxy_max_temp_file_size 0;  

  #access_log /var/log/nginx/docker-registry-access.log;
  error_log /var/log/nginx/docker-registry-error.log;

  ssl_certificate /etc/letsencrypt/live/myserver.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/myserver.com/privkey.pem;

  location / {

        auth_basic "Registry Realm";
        auth_basic_user_file /etc/docker-registry-htpasswd;  

        proxy_pass http://docker-registry/;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file goes in "/etc/nginx/sites-available/docker-registry", with a symbolic link in "/etc/nginx/sites-enabled/":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ln /etc/nginx/sites-available/docker-registry /etc/nginx/sites-enabled/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need to change a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server  {
  listen 10000 ssl;
  server_name    myserver.com;
  ssl_certificate /etc/letsencrypt/live/myserver.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/myserver.com/privkey.pem;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the listen port with the port you want your registry to run on. Replace "myserver.com" in all 3 places with the domain name you are using. If you are not using Let's Encrypt then replace the whole path with where you keep your SSL certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    auth_basic "Registry Realm";
    auth_basic_user_file /etc/docker-registry-htpasswd;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section adds password protection to your registry. It is using a standard htpasswd file. To create this file follow these steps (Debian based only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install apache2-utils
htpasswd -c /etc/docker-registry-htpasswd username
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace "username" with whatever you want for a name. You will be prompted for a password. You can add additional users by repeating the command without the "-c".&lt;/p&gt;

&lt;p&gt;After you've create the file and made the necessary changes, you can test your ngix configuration sanity using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nginx -t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the configuration is acceptable you'll see messages like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the NGINX configuration test works you can restart NGINX using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the Registry
&lt;/h2&gt;

&lt;p&gt;To begin using your private registry you will need to login which creates the "~.docker/config.json" file. From the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker login myserver.com:PORT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replacing "myserver.com" and "port" with your values. You will prompted for the user name and password.&lt;/p&gt;

&lt;p&gt;Now we're finally ready to use the registry! First we'll need to push an image to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker push myserver.com:port/yourimage:tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pull:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull myserver.com:port/yourimage:tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those two commands work for your private registry, but if you want to pull a public image from Docker Hub you will have to adjust the normal image path to include "/library/" like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull myserver.com:port/library/publicimage:tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"/library" is the base path for all Docker Hub images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this post I've shown how to configure a private Docker registry.  The registry uses Docker Hub as a backing store for public images. NGINX is used to provide SSL encryption and also password protect the private registry. &lt;/p&gt;

</description>
      <category>docker</category>
      <category>registry</category>
      <category>hosting</category>
      <category>private</category>
    </item>
    <item>
      <title>Packet Capture From Inside Docker</title>
      <dc:creator>Kirby Angell</dc:creator>
      <pubDate>Sun, 10 Mar 2024 19:26:42 +0000</pubDate>
      <link>https://dev.to/kangell/packet-capture-from-inside-docker-348n</link>
      <guid>https://dev.to/kangell/packet-capture-from-inside-docker-348n</guid>
      <description>&lt;p&gt;For an existing project I needed a better way of capturing network traffic than what I had been using. Before, I used Python to setup a capture using Impacket. I would have to keep track of the ports the process used and then filter the pcap data based on those ports to get just the process I was interested in. That method had a big problem though. The port data had to be collected from the connections themselves which meant until the connection was setup, I wouldn't see any of the traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Looking for a new solution what I wanted was something like &lt;em&gt;strace&lt;/em&gt; but instead of recording library function calls I want the network packets being sent and in pcap format. I found what I was looking for in an &lt;a href="https://askubuntu.com/questions/11709/how-can-i-capture-network-traffic-of-a-single-process"&gt;AskUbuntu post&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;A lot has changed in 13 years since that post was written, but how Linux does networking, at least from the administrators side is still valid. A response to the question has &lt;a href="https://askubuntu.com/questions/11709/how-can-i-capture-network-traffic-of-a-single-process/499850#499850"&gt;the answer&lt;/a&gt; which is to use some iptables magic to create an isolated network namespace. That author points out that it is fairly complicated though. It might also be hard to "wrap up" into a usable tool.&lt;/p&gt;

&lt;p&gt;But it wasn't! At least not for Jonas Danielsson who &lt;a href="https://askubuntu.com/a/798919"&gt;followed up&lt;/a&gt; with a Github project for the tool &lt;a href="https://github.com/nsntrace/nsntrace"&gt;nsntrace&lt;/a&gt;. So that's what I'm going to use in this article where I'll show you how to capture all of the network traffic into a pcap file for a process spawned inside of a Docker container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Configuration
&lt;/h2&gt;

&lt;p&gt;Here is the basic Dockerfile configuration we will be using for this project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use Debian as the base image
FROM debian:buster

# Update package lists and install necessary packages
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
    curl \
    build-essential \
    git \
    automake \
    docbook-xml  \
    docbook-xsl \
    iptables \
    libnl-route-3-dev \
    libpcap-dev \
    pkg-config \
    xsltproc \
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*

RUN update-alternatives --set iptables /usr/sbin/iptables-legacy
RUN update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy

WORKDIR /
RUN git clone https://github.com/nsntrace/nsntrace.git
WORKDIR /nsntrace
RUN ./autogen.sh
RUN ./configure
RUN make install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual one for my project involves quite a bit more, but this has the essentials we'll need to get &lt;em&gt;nsntrace&lt;/em&gt; working. The package list comes from the &lt;em&gt;nstrace&lt;/em&gt; build instructions plus a few extras to facilitate the build and testing. Debian as of the Buster release started using &lt;em&gt;nftables&lt;/em&gt; instead of &lt;em&gt;iptables&lt;/em&gt;. Luckily Debian provides an easy way to switch back to &lt;em&gt;iptables&lt;/em&gt; using the &lt;strong&gt;update-alternatives&lt;/strong&gt; command in the Dockerfile.&lt;/p&gt;

&lt;p&gt;We build the container using this command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t test .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Running
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker run \
    --privileged \
    -v /tmp:/tmp -it test nsntrace \
    &amp;lt;process to run&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For &lt;em&gt;nstrace&lt;/em&gt; to do the things it needs to capture the traffic, such as adding network interfaces and manipulating the firewall rules it needs the "--privileged" flag. I'm not a big fan of giving the container these capabilities and would like to narrow it down. With SYS_ADMIN and NET_ADMIN &lt;em&gt;nstrace&lt;/em&gt; works, but displays an error message "failed to set namespace name". As of the time of this publiciation I haven't found a Docker &lt;strong&gt;run&lt;/strong&gt; flag that will give it just the ability to control network namespaces.&lt;/p&gt;

&lt;p&gt;I've chosen to map the host's /tmp directory to the container's /tmp directory so we can get the packet captures back out of the container to examine. Here I use the new container to capture the traffic &lt;strong&gt;curl&lt;/strong&gt; generates accessing my company website:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker run --privileged -v /tmp:/tmp -it test nsntrace -o /tmp/test.cap curl https://alertra.com
Starting network trace of 'curl' on interface eth0.
Your IP address in this trace is 172.19.179.255.
Use ctrl-c to end at any time.

&amp;lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"&amp;gt;
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;301 Moved Permanently&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Moved Permanently&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;The document has moved &amp;lt;a href="https://www.alertra.com/"&amp;gt;here&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
Finished capturing 33 packets.
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The pcap file is placed in /tmp/test.cap which we can examine with Wireshark:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2xj35kyhzxqqun4pbrx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2xj35kyhzxqqun4pbrx.png" alt="pcap" width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
Here we see everything the process did on the network. The DNS queries, the setup and teardown of the TCP connection, and the TLS negotiation. We even see the local network ARP packets!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Obviously this is a simple example and if you just want to capture packets coming or going on a port then there are easier ways to do it like &lt;strong&gt;tcpdump&lt;/strong&gt; and &lt;strong&gt;Wireshark&lt;/strong&gt;. But if you really need to see all the network traffic at a process level, then &lt;em&gt;nsntrace&lt;/em&gt; works great and can be run from within a Docker container if need be.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nsntrace</category>
      <category>pcap</category>
      <category>wireshark</category>
    </item>
  </channel>
</rss>
