Browse Source

Merge pull request #899 from gravitl/develop

Upgrade to v0.12.0
dcarns 3 years ago
parent
commit
aa821e0304

+ 8 - 0
.fpm

@@ -0,0 +1,8 @@
+--name netclient 
+--license sspl 
+--depends wireguard-tools 
+--description "Netmaker's netclient agent and CLI" 
+--url "https//:github.com/gravitl/netmaker" 
+--maintainer "[email protected]" 
+--vendor Gravitl
+

+ 72 - 0
.github/ISSUE_TEMPLATE/bug-report.yml

@@ -0,0 +1,72 @@
+name: Bug Report
+description: File a bug report
+title: "[Bug]: "
+labels: ["bug", "triage"]
+assignees: ["ok-john", "0xdcarns", "afeiszli",  "mattkasun"]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Thanks for taking the time to fill out this bug report!
+  - type: input
+    id: contact
+    attributes:
+      label: Contact Details
+      description: How can we get in touch with you if we need more info?
+      placeholder: ex. [email protected]
+    validations:
+      required: false
+  - type: textarea
+    id: what-happened
+    attributes:
+      label: What happened?
+      description: Also tell us, what did you expect to happen?
+      placeholder: Tell us what you see!
+      value: "A bug happened!"
+    validations:
+      required: true
+  - type: dropdown
+    id: version
+    attributes:
+      label: Version
+      description: What version are you running?
+      options:
+        - v0.12.0
+        - v0.11.1
+        - v0.11.0
+        - v0.10.0
+        - v0.9.4
+        - v0.9.3
+        - v0.9.2
+        - v0.9.1
+        - v0.9.0
+        - v0.8.5
+        - Not sure
+        - Not listed
+    validations:
+      required: true
+  - type: dropdown
+    id: os
+    attributes:
+      label: What OS are you using?
+      multiple: true
+      options:
+        - Linux
+        - FreeBSD
+        - Windows
+        - Mac
+        - Unlisted
+  - type: textarea
+    id: logs
+    attributes:
+      label: Relevant log output
+      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+      render: shell
+  - type: checkboxes
+    id: terms
+    attributes:
+      label: Contributing guidelines
+      description: Have you read [CONTRIBUTING.md](https://github.com/gravitl/netmaker/blob/master/CONTRIBUTING.md)
+      options:
+        - label: Yes, I did.
+          required: true

+ 235 - 29
.github/workflows/buildandrelease.yml

@@ -5,16 +5,17 @@ on:
     inputs:
       version:
         description: 'Netmaker version'
-        required: false
+        required: true
   release:
     types: [published]
 
 jobs:
-  build:
+  version:
     runs-on: ubuntu-latest
-
+    outputs:
+      tag: ${{ steps.echo.outputs.tag }}
+      version: ${{ steps.echo.outputs.version }}
     steps:
-
       - name: Get Version Number
         run: |
           if [[ -n "${{ github.event.inputs.version }}" ]]; then
@@ -23,32 +24,43 @@ jobs:
             NETMAKER_VERSION=$(curl -fsSL https://api.github.com/repos/gravitl/netmaker/tags | grep 'name' | head -1 | cut -d'"' -f4)
           fi
           echo "NETMAKER_VERSION=${NETMAKER_VERSION}" >> $GITHUB_ENV
+          # remove everything but digits and . for package (deb, rpm, etc) versions
+          PACKAGE_VERSION=$(echo ${NETMAKER_VERSION} | tr -cd '[:digit:].')
+          echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >> $GITHUB_ENV
+      - name: Echo
+        id: echo
+        run: |
+          echo ${{ env.NETMAKER_VERSION }}
+          echo ${{ env.PACKAGE_VERSION }}
+          if [[ -z ${{ env.NETMAKER_VERSION }} || -z ${{ env.PACKAGE_VERSION }} ]]
+          then
+            exit 1
+          fi
+          echo "::set-output name=tag::${{ env.NETMAKER_VERSION }}"
+          echo "::set-output name=version::${{ env.PACKAGE_VERSION }}"
+  netmaker:        
+    runs-on: ubuntu-latest
+    needs: version
+    steps:
+      - name: set variables
+        run: |
+          echo ${{ needs.version.outputs.tag }} ${{ needs.version.outputs.version }}
+          TAG=${{needs.version.outputs.tag}}
+          VERSION=${{needs.version.outputs.version}}
+          if [[ -z ${VERSION} || -z ${TAG} ]]; then
+            exit 1
+          fi
+          echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
+          echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Checkout
         uses: actions/checkout@v2
-
       - name: Setup go
         uses: actions/setup-go@v2
         with:
           go-version: 1.17
-
       - name: Build
         run: |
           env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netmaker main.go
-          cd netclient
-          env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm5/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm6/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm7/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm64/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w -X 'main.version=$NETMAKER_VERSION'" -o build/netclient-mipsle/netclient main.go && upx build/netclient-mipsle/netclient
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags="-X 'main.Version=${NETMAKER_VERSION}'" -o build/netclient-freebsd/netclient main.go
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=5 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm5/netclient main.go
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=6 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm6/netclient main.go
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm7/netclient main.go
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm64/netclient main.go
-          env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin/netclient main.go
-          env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-arm64/netclient main.go
-          
       - name: Upload netmaker x86 to Release
         uses: svenstaro/upload-release-action@v2
         with:
@@ -59,7 +71,28 @@ jobs:
           prerelease: true
           asset_name: netmaker
 
-      - name: Upload x86 to Release
+  netclient-x86:
+    runs-on: ubuntu-latest
+    needs: version
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Set Variables
+        run: |
+          TAG=${{needs.version.outputs.tag}}
+          VERSION=${{needs.version.outputs.version}}
+          echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
+          echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
+      - name: Setup go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+      - name: Build
+        run: |
+          cd netclient
+          env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient main.go
+      - name: Upload netclient x86 to Release
+        continue-on-error: true
         uses: svenstaro/upload-release-action@v2
         with:
           repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -69,26 +102,100 @@ jobs:
           prerelease: true
           asset_name: netclient
 
-      - name: Upload arm5 to Release
+      - name: Package x86 deb
+        continue-on-error: true
+        uses: gravitl/github-action-fpm@master
+        with:
+          fpm_args: './netclient/build/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/system/netclient.service'
+          fpm_opts: '-s dir -t deb --architecture amd64 --version ${{ env.PACKAGE_VERSION }}'
+      - name: Upload x86 deb to Release
+        continue-on-error: true
         uses: svenstaro/upload-release-action@v2
         with:
           repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-arm5/netclient
+          file: netclient_${{ env.PACKAGE_VERSION }}_amd64.deb
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient_${{ env.PACKAGE_VERSION }}_amd64.deb
+
+      - name: Package x86 rpm
+        continue-on-error: true
+        uses: gravitl/github-action-fpm@master
+        with:
+          fpm_args: './netclient/build/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/system/netclient.service'
+          fpm_opts: '-s dir -t rpm --architecture amd64 --version ${{ env.PACKAGE_VERSION }}'
+      - name: Upload x86 rpm to Release
+        continue-on-error: true
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient-${{ env.PACKAGE_VERSION }}-1.x86_64.rpm 
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-${{ env.PACKAGE_VERSION }}-1.x86_64.rpm
+
+      - name: Package x86 pacman
+        continue-on-error: true
+        uses: gravitl/github-action-fpm@master
+        with:
+          # arch has particular path requirements --- cannot write to a symbolic link e.g. /sbin and /lib
+          fpm_args: './netclient/build/netclient=/usr/bin/netclient ./netclient/build/netclient.service=/usr/lib/systemd/system/netclient.service'
+          fpm_opts: '-s dir -t pacman --architecture amd64 --version ${{ env.PACKAGE_VERSION }}'
+      - name: Upload x86 pacman to Release
+        continue-on-error: true
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient-${{ env.PACKAGE_VERSION }}-1-x86_64.pkg.tar.zst
           tag: ${{ env.NETMAKER_VERSION }}
           overwrite: true
           prerelease: true
+          asset_name: netclient-${{ env.PACKAGE_VERSION }}-1-x86_64.pkg.tar.zst
+
+  netclient-arm:
+    runs-on: ubuntu-latest
+    needs: version
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Set Variables
+        run: |
+          TAG=${{needs.version.outputs.tag}}
+          VERSION=${{needs.version.outputs.version}}
+          echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
+          echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
+      - name: Setup go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+      - name: Build
+        run: |
+          cd netclient
+          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm5/netclient main.go
+          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm6/netclient main.go
+          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm7/netclient main.go
+          env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm64/netclient main.go
+      - name: Upload arm5 to Release
+        uses: svenstaro/upload-release-action@v2
+        with: 
+          repo_token: ${{ secrets.GITHUB_TOKEN }} 
+          file: netclient/build/netclient-arm5/netclient
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true 
+          prerelease: true
           asset_name: netclient-arm5
 
       - name: Upload arm6 to Release
         uses: svenstaro/upload-release-action@v2
-        with:
+        with: 
           repo_token: ${{ secrets.GITHUB_TOKEN }}
           file: netclient/build/netclient-arm6/netclient
           tag: ${{ env.NETMAKER_VERSION }}
           overwrite: true
           prerelease: true
           asset_name: netclient-arm6
-
       - name: Upload arm7 to Release
         uses: svenstaro/upload-release-action@v2
         with:
@@ -98,8 +205,9 @@ jobs:
           overwrite: true
           prerelease: true
           asset_name: netclient-arm7
-
+     
       - name: Upload arm64 to Release
+        continue-on-error: true
         uses: svenstaro/upload-release-action@v2
         with:
           repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -108,7 +216,59 @@ jobs:
           overwrite: true
           prerelease: true
           asset_name: netclient-arm64
+      - name: Package arm64 deb
+        continue-on-error: true
+        uses: gravitl/github-action-fpm@master
+        with:
+          fpm_args: './netclient/build/netclient-arm64/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/netclient.service'
+          fpm_opts: '-s dir -t deb --architecture arm64 --version ${{ env.PACKAGE_VERSION }}'
+      - name: Upload arm deb to Release
+        continue-on-error: true
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient_${{ env.PACKAGE_VERSION }}_arm64.deb
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient_${{ env.PACKAGE_VERSION }}_arm64.deb
+      - name: Package arm64 rpm
+        continue-on-error: true
+        uses: gravitl/github-action-fpm@master
+        with:
+          fpm_args: './netclient/build/netclient-arm64/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/netclient.service'
+          fpm_opts: '-s dir -t rpm --architecture arm64 --version ${{ env.PACKAGE_VERSION }}'
+      - name: Upload arm64 rpm to Release
+        continue-on-error: true
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient-${{ env.PACKAGE_VERSION }}-1.aarch64.rpm
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-${{ env.PACKAGE_VERSION }}-1.aarch4.rpm
 
+  netclient-mipsle:
+    runs-on: ubuntu-latest
+    needs: version
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Set Variables
+        run: |
+          TAG=${{needs.version.outputs.tag}}
+          VERSION=${{needs.version.outputs.version}}
+          echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
+          echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
+      - name: Setup go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+      - name: Build
+        run: |
+          cd netclient
+          env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w -X 'main.version=$NETMAKER_VERSION'" -o build/netclient-mipsle/netclient main.go && upx build/netclient-mipsle/netclient
       - name: Upload mipsle to Release
         uses: svenstaro/upload-release-action@v2
         with:
@@ -118,6 +278,31 @@ jobs:
           overwrite: true
           prerelease: true
           asset_name: netclient-mipsle
+   
+  netclient-freebsd:
+    runs-on: ubuntu-latest
+    needs: version
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Set Variables
+        run: |
+          TAG=${{needs.version.outputs.tag}}
+          VERSION=${{needs.version.outputs.version}}
+          echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
+          echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
+      - name: Setup go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+      - name: Build
+        run: |
+          cd netclient
+          env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags="-X 'main.Version=${NETMAKER_VERSION}'" -o build/netclient-freebsd/netclient main.go
+          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=5 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm5/netclient main.go
+          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=6 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm6/netclient main.go
+          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm7/netclient main.go
+          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm64/netclient main.go
 
       - name: Upload freebsd to Release
         uses: svenstaro/upload-release-action@v2
@@ -168,8 +353,29 @@ jobs:
           overwrite: true
           prerelease: true
           asset_name: netclient-freebsd-arm64
-          
-      - name: Upload darwin to Release
+
+  netclient-darwin:
+    runs-on: ubuntu-latest
+    needs: version
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Set Variables
+        run: |
+          TAG=${{needs.version.outputs.tag}}
+          VERSION=${{needs.version.outputs.version}}
+          echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
+          echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
+      - name: Setup go                                    
+        uses: actions/setup-go@v2                        
+        with:                                           
+          go-version: 1.17                             
+      - name: Build                                   
+        run: |                                       
+          cd netclient                              
+          env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin/netclient main.go
+          env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-arm64/netclient main.go
+      - name: Upload darwin-arm64 to Release
         uses: svenstaro/upload-release-action@v2
         with:
           repo_token: ${{ secrets.GITHUB_TOKEN }}

+ 5 - 4
.github/workflows/purgeGHCR.yml

@@ -1,8 +1,9 @@
 name: Purge untagged images from GHCR
 
 on: 
-    workflow_dispatch
-
+    workflow_dispatch:
+    schedule:
+      - cron: '1 1 1 * *'
 jobs:
   purge:
     runs-on: ubuntu-latest
@@ -15,7 +16,7 @@ jobs:
           token: ${{ secrets.GITHUB_TOKEN }}
           organization: gravitl
           container: netmaker
-          dry-run: true # Dry-run first, then change to `false`
+          dry-run: false # Dry-run first, then change to `false`
           untagged: true
       - name: Prune Netclient
         uses: vlaurin/action-ghcr-prune@main
@@ -23,6 +24,6 @@ jobs:
           token: ${{ secrets.GITHUB_TOKEN }}
           organization: gravitl
           container: netclient
-          dry-run: true # Dry-run first, then change to `false`
+          dry-run: false # Dry-run first, then change to `false`
           untagged: true
           

+ 3 - 0
.gitignore

@@ -6,6 +6,7 @@ netmaker-amd64
 netclient/netclient
 netclient/build
 netclient/build/
+!netclient/build/netclient.service
 netclient/files/netclient
 netclient/netclient-amd64
 netclient/netclient-arm
@@ -14,6 +15,8 @@ netclient/netclient-32
 netclient/netclient32
 netclient/netclient.exe
 config/dnsconfig/
+controllers/config/dnsconfig/
+controllers/data/
 data/
 .vscode/
 .idea/

+ 2 - 2
README.md

@@ -3,12 +3,12 @@
   <img src="netmaker.png" width="75%"><break/>
 </p>
 <p align="center">
-<i>Create and control automated virtual networks.</i> 
+a platform for blazing fast and dynamic virtual networks 
 </p>
 
 <p align="center">
   <a href="https://github.com/gravitl/netmaker/releases">
-    <img src="https://img.shields.io/badge/Version-0.11.1-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.12.0-informational?style=flat-square" />
   </a>
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker" />

+ 2 - 2
compose/docker-compose.contained.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.11.1
+    image: gravitl/netmaker:v0.12.0
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -45,7 +45,7 @@ services:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.11.1
+    image: gravitl/netmaker-ui:v0.12.0
     links:
       - "netmaker:api"
     ports:

+ 2 - 2
compose/docker-compose.hostnetwork.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.11.1
+    image: gravitl/netmaker:v0.12.0
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - /usr/bin/wg:/usr/bin/wg
@@ -41,7 +41,7 @@ services:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:0.11.1
+    image: gravitl/netmaker-ui:0.12.0
     links:
       - "netmaker:api"
     ports:

+ 2 - 2
compose/docker-compose.nocaddy.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.11.1
+    image: gravitl/netmaker:v0.12.0
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -45,7 +45,7 @@ services:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.11.1
+    image: gravitl/netmaker-ui:v0.12.0
     links:
       - "netmaker:api"
     ports:

+ 2 - 2
compose/docker-compose.nodns.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.11.1
+    image: gravitl/netmaker:v0.12.0
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -45,7 +45,7 @@ services:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.11.1
+    image: gravitl/netmaker-ui:v0.12.0
     links:
       - "netmaker:api"
     ports:

+ 2 - 2
compose/docker-compose.reference.yml

@@ -2,7 +2,7 @@ services:
   netmaker: # The Primary Server for running Netmaker
     privileged: true # Necessary to run sudo/root level commands on host system. Likely using this if running with host networking on.
     container_name: netmaker
-    image: gravitl/netmaker:v0.11.1
+    image: gravitl/netmaker:v0.12.0
     volumes: # Volume mounts necessary for CLIENT_MODE to control wireguard networking on host (except dnsconfig, which is where dns config files are stored for use by CoreDNS)
       - dnsconfig:/root/config/dnsconfig # Netmaker writes Corefile to this location, which gets mounted by CoreDNS for DNS configuration.
       - sqldata:/root/data
@@ -44,7 +44,7 @@ services:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.11.1
+    image: gravitl/netmaker-ui:v0.12.0
     links:
       - "netmaker:api"
     ports:

+ 2 - 2
compose/docker-compose.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.11.1
+    image: gravitl/netmaker:v0.12.0
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -45,7 +45,7 @@ services:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.11.1
+    image: gravitl/netmaker-ui:v0.12.0
     links:
       - "netmaker:api"
     ports:

+ 1 - 2
controllers/config/dnsconfig/netmaker.hosts

@@ -1,2 +1 @@
-10.0.0.1         testnode.skynet
-10.0.0.2         myhost.skynet
+10.0.0.2         testnode.skynet myhost.skynet

+ 21 - 3
controllers/ext_client.go

@@ -151,9 +151,15 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	if network.DefaultExtClientDNS != "" {
 		defaultDNS = "DNS = " + network.DefaultExtClientDNS
 	}
+
+	defaultMTU := 1420
+	if gwnode.MTU != 0 {
+		defaultMTU = int(gwnode.MTU)
+	}
 	config := fmt.Sprintf(`[Interface]
 Address = %s
 PrivateKey = %s
+MTU = %d
 %s
 
 [Peer]
@@ -164,6 +170,7 @@ Endpoint = %s
 
 `, client.Address+"/32",
 		client.PrivateKey,
+		defaultMTU,
 		defaultDNS,
 		gwnode.PublicKey,
 		newAllowedIPs,
@@ -228,6 +235,8 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	extclient.IngressGatewayEndpoint = node.Endpoint + ":" + strconv.FormatInt(int64(node.ListenPort), 10)
+	// TODO, could rely on network template as well in future
+	extclient.Enabled = true
 	err = json.NewDecoder(r.Body).Decode(&extclient)
 	if err != nil && !errors.Is(err, io.EOF) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -238,6 +247,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	logger.Log(0, r.Header.Get("user"), "created new ext client on network", networkName)
 	w.WriteHeader(http.StatusOK)
 	err = mq.PublishExtPeerUpdate(&node)
 	if err != nil {
@@ -268,12 +278,20 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	newclient, err := logic.UpdateExtClient(newExtClient.ClientID, params["network"], &oldExtClient)
+	var changedEnabled = newExtClient.Enabled != oldExtClient.Enabled // indicates there was a change in enablement
+	newclient, err := logic.UpdateExtClient(newExtClient.ClientID, params["network"], newExtClient.Enabled, &oldExtClient)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	logger.Log(1, r.Header.Get("user"), "updated client", newExtClient.ClientID)
+	logger.Log(0, r.Header.Get("user"), "updated ext client", newExtClient.ClientID)
+	if changedEnabled { // need to send a peer update to the ingress node as enablement of one of it's clients has changed
+		if ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil {
+			if err = mq.PublishExtPeerUpdate(&ingressNode); err != nil {
+				logger.Log(1, "error setting ext peers on", ingressNode.ID, ":", err.Error())
+			}
+		}
+	}
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(newclient)
 }
@@ -311,7 +329,7 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		logger.Log(1, "error setting ext peers on "+ingressnode.ID+": "+err.Error())
 	}
-	logger.Log(1, r.Header.Get("user"),
+	logger.Log(0, r.Header.Get("user"),
 		"Deleted extclient client", params["clientid"], "from network", params["network"])
 	returnSuccessResponse(w, r, params["clientid"]+" deleted.")
 }

+ 56 - 0
controllers/network.go

@@ -11,6 +11,7 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/logic/acls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/servercfg"
@@ -34,6 +35,9 @@ func networkHandlers(r *mux.Router) {
 	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(false, http.HandlerFunc(createAccessKey))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(false, http.HandlerFunc(getAccessKeys))).Methods("GET")
 	r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(false, http.HandlerFunc(deleteAccessKey))).Methods("DELETE")
+	// ACLs
+	r.HandleFunc("/api/networks/{networkname}/acls", securityCheck(true, http.HandlerFunc(updateNetworkACL))).Methods("PUT")
+	r.HandleFunc("/api/networks/{networkname}/acls", securityCheck(true, http.HandlerFunc(getNetworkACL))).Methods("GET")
 }
 
 //simple get all networks function
@@ -231,6 +235,58 @@ func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(network)
 }
 
+func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	netname := params["networkname"]
+	var networkACLChange acls.ACLContainer
+	networkACLChange, err := networkACLChange.Get(acls.ContainerID(netname))
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	_ = json.NewDecoder(r.Body).Decode(&networkACLChange)
+	newNetACL, err := networkACLChange.Save(acls.ContainerID(netname))
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
+	}
+	logger.Log(1, r.Header.Get("user"), "updated ACLs for network", netname)
+
+	// send peer updates
+	if servercfg.IsMessageQueueBackend() {
+		serverNode, err := logic.GetNetworkServerLocal(netname)
+		if err != nil {
+			logger.Log(1, "failed to find server node after ACL update on", netname)
+		} else {
+			if err = logic.ServerUpdate(&serverNode, false); err != nil {
+				logger.Log(1, "failed to update server node after ACL update on", netname)
+			}
+			if err = mq.PublishPeerUpdate(&serverNode); err != nil {
+				logger.Log(0, "failed to publish peer update after ACL update on", netname)
+			}
+		}
+	}
+
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(newNetACL)
+}
+
+func getNetworkACL(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	netname := params["networkname"]
+	var networkACL acls.ACLContainer
+	networkACL, err := networkACL.Get(acls.ContainerID(netname))
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	logger.Log(2, r.Header.Get("user"), "fetched acl for network", netname)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(networkACL)
+}
+
 // Delete a network
 // Will stop you if  there's any nodes associated
 func deleteNetwork(w http.ResponseWriter, r *http.Request) {

+ 0 - 1
controllers/node.go

@@ -34,7 +34,6 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
-
 }
 
 func authenticate(response http.ResponseWriter, request *http.Request) {

+ 68 - 0
controllers/node_test.go

@@ -5,6 +5,8 @@ import (
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
 )
@@ -143,6 +145,72 @@ func TestValidateEgressGateway(t *testing.T) {
 	})
 }
 
+func TestNodeACLs(t *testing.T) {
+	deleteAllNodes()
+	node1 := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.50", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux"}
+	node2 := models.Node{PublicKey: "DM5qhLAE20FG7BbfBCger+Ac9D2NDOwCtY1rbYDXf14=", Name: "testnode", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
+	logic.CreateNode(&node1)
+	logic.CreateNode(&node2)
+	t.Run("acls not present", func(t *testing.T) {
+		currentACL, err := nodeacls.FetchAllACLs(nodeacls.NetworkID(node1.Network))
+		assert.Nil(t, err)
+		assert.NotNil(t, currentACL)
+		node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
+		assert.Nil(t, err)
+		assert.NotNil(t, node1ACL)
+		assert.Equal(t, acls.Allowed, node1ACL[acls.AclID(node2.ID)])
+	})
+	t.Run("node acls exists after creates", func(t *testing.T) {
+		node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
+		assert.Nil(t, err)
+		assert.NotNil(t, node1ACL)
+		node2ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node2.Network), nodeacls.NodeID(node2.ID))
+		assert.Nil(t, err)
+		assert.NotNil(t, node2ACL)
+		assert.Equal(t, acls.Allowed, node2ACL[acls.AclID(node1.ID)])
+	})
+	t.Run("node acls correct after fetch", func(t *testing.T) {
+		node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
+		assert.Nil(t, err)
+		assert.Equal(t, acls.Allowed, node1ACL[acls.AclID(node2.ID)])
+	})
+	t.Run("node acls correct after modify", func(t *testing.T) {
+		node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
+		assert.Nil(t, err)
+		assert.NotNil(t, node1ACL)
+		node2ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node2.Network), nodeacls.NodeID(node2.ID))
+		assert.Nil(t, err)
+		assert.NotNil(t, node2ACL)
+		currentACL, err := nodeacls.DisallowNodes(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID), nodeacls.NodeID(node2.ID))
+		assert.Nil(t, err)
+		assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node1.ID)][acls.AclID(node2.ID)])
+		assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node2.ID)][acls.AclID(node1.ID)])
+		currentACL.Save(acls.ContainerID(node1.Network))
+	})
+	t.Run("node acls correct after add new node not allowed", func(t *testing.T) {
+		node3 := models.Node{PublicKey: "DM5qhLAE20FG7BbfBCger+Ac9D2NDOwCtY1rbYDXv24=", Name: "testnode3", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
+		logic.CreateNode(&node3)
+		var currentACL, err = nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
+		assert.Nil(t, err)
+		assert.NotNil(t, currentACL)
+		assert.Equal(t, acls.NotPresent, currentACL[acls.AclID(node1.ID)][acls.AclID(node3.ID)])
+		nodeACL, err := nodeacls.CreateNodeACL(nodeacls.NetworkID(node3.Network), nodeacls.NodeID(node3.ID), acls.NotAllowed)
+		assert.Nil(t, err)
+		nodeACL.Save(acls.ContainerID(node3.Network), acls.AclID(node3.ID))
+		currentACL, err = nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
+		assert.Nil(t, err)
+		assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node1.ID)][acls.AclID(node3.ID)])
+		assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node2.ID)][acls.AclID(node3.ID)])
+	})
+	t.Run("node acls removed", func(t *testing.T) {
+		retNetworkACL, err := nodeacls.RemoveNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
+		assert.Nil(t, err)
+		assert.NotNil(t, retNetworkACL)
+		assert.Equal(t, acls.NotPresent, retNetworkACL[acls.AclID(node2.ID)][acls.AclID(node1.ID)])
+	})
+	deleteAllNodes()
+}
+
 func deleteAllNodes() {
 	database.DeleteAllRecords(database.NODES_TABLE_NAME)
 }

+ 4 - 0
database/database.go

@@ -50,6 +50,9 @@ const DATABASE_FILENAME = "netmaker.db"
 // GENERATED_TABLE_NAME - stores server generated k/v
 const GENERATED_TABLE_NAME = "generated"
 
+// NODE_ACLS_TABLE_NAME - stores the node ACL rules
+const NODE_ACLS_TABLE_NAME = "nodeacls"
+
 // == ERROR CONSTS ==
 
 // NO_RECORD - no singular result found
@@ -127,6 +130,7 @@ func createTables() {
 	createTable(SERVERCONF_TABLE_NAME)
 	createTable(SERVER_UUID_TABLE_NAME)
 	createTable(GENERATED_TABLE_NAME)
+	createTable(NODE_ACLS_TABLE_NAME)
 }
 
 func createTable(tableName string) error {

+ 5 - 5
go.mod

@@ -4,7 +4,7 @@ go 1.17
 
 require (
 	github.com/eclipse/paho.mqtt.golang v1.3.5
-	github.com/go-playground/validator/v10 v10.10.0
+	github.com/go-playground/validator/v10 v10.10.1
 	github.com/golang-jwt/jwt/v4 v4.3.0
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/uuid v1.3.0
@@ -17,15 +17,15 @@ require (
 	github.com/stretchr/testify v1.7.0
 	github.com/txn2/txeh v1.3.0
 	github.com/urfave/cli/v2 v2.3.0
-	golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
-	golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect
+	golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
+	golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 	golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect
-	golang.org/x/text v0.3.7-0.20210524175448-3115f89c4b99 // indirect
+	golang.org/x/text v0.3.7 // indirect
 	golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19 // indirect
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210913210325-91d1988e44de
 	google.golang.org/genproto v0.0.0-20210201151548-94839c025ad4 // indirect
-	google.golang.org/grpc v1.44.0
+	google.golang.org/grpc v1.45.0
 	google.golang.org/protobuf v1.27.1
 	gopkg.in/ini.v1 v1.66.4
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b

+ 10 - 10
go.sum

@@ -69,8 +69,8 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb
 github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
 github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
 github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
-github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
-github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
+github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
+github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -275,8 +275,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -304,8 +304,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
-golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -355,8 +355,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7-0.20210524175448-3115f89c4b99 h1:ZEXtoJu1S0ie/EmdYnjY3CqaCCZxnldL+K1ftMITD2Q=
-golang.org/x/text v0.3.7-0.20210524175448-3115f89c4b99/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -389,8 +389,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
-google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
+google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

+ 124 - 0
logic/acls/common.go

@@ -0,0 +1,124 @@
+package acls
+
+import (
+	"encoding/json"
+
+	"github.com/gravitl/netmaker/database"
+)
+
+// == type functions ==
+
+// ACL.Allow - allows access by ID in memory
+func (acl ACL) Allow(ID AclID) {
+	acl[ID] = Allowed
+}
+
+// ACL.DisallowNode - disallows access by ID in memory
+func (acl ACL) Disallow(ID AclID) {
+	acl[ID] = NotAllowed
+}
+
+// ACL.Remove - removes a node from a ACL in memory
+func (acl ACL) Remove(ID AclID) {
+	delete(acl, ID)
+}
+
+// ACL.Update - updates a ACL in DB
+func (acl ACL) Save(containerID ContainerID, ID AclID) (ACL, error) {
+	return upsertACL(containerID, ID, acl)
+}
+
+// ACL.IsAllowed - sees if ID is allowed in referring ACL
+func (acl ACL) IsAllowed(ID AclID) bool {
+	return acl[ID] == Allowed
+}
+
+// ACLContainer.IsAllowed - returns if the current ACL container contains allowed ACLs between two IDs
+func (aclContainer ACLContainer) IsAllowed(ID1, ID2 AclID) bool {
+	return aclContainer[ID1].IsAllowed(ID2) && aclContainer[ID2].IsAllowed(ID1)
+}
+
+// ACLContainer.UpdateACL - saves the state of a ACL in the ACLContainer in memory
+func (aclContainer ACLContainer) UpdateACL(ID AclID, acl ACL) ACLContainer {
+	aclContainer[ID] = acl
+	return aclContainer
+}
+
+// ACLContainer.RemoveACL - removes the state of a ACL in the ACLContainer in memory
+func (aclContainer ACLContainer) RemoveACL(ID AclID) ACLContainer {
+	delete(aclContainer, ID)
+	return aclContainer
+}
+
+// ACLContainer.ChangeAccess - changes the relationship between two nodes in memory
+func (networkACL ACLContainer) ChangeAccess(ID1, ID2 AclID, value byte) {
+	networkACL[ID1][ID2] = value
+	networkACL[ID2][ID1] = value
+}
+
+// ACLContainer.Save - saves the state of a ACLContainer to the db
+func (aclContainer ACLContainer) Save(containerID ContainerID) (ACLContainer, error) {
+	return upsertACLContainer(containerID, aclContainer)
+}
+
+// ACLContainer.New - saves the state of a ACLContainer to the db
+func (aclContainer ACLContainer) New(containerID ContainerID) (ACLContainer, error) {
+	return upsertACLContainer(containerID, nil)
+}
+
+// ACLContainer.Get - saves the state of a ACLContainer to the db
+func (aclContainer ACLContainer) Get(containerID ContainerID) (ACLContainer, error) {
+	return fetchACLContainer(containerID)
+}
+
+// == private ==
+
+// fetchACLContainer - fetches all current rules in given ACL container
+func fetchACLContainer(containerID ContainerID) (ACLContainer, error) {
+	aclJson, err := fetchACLContainerJson(ContainerID(containerID))
+	if err != nil {
+		return nil, err
+	}
+	var currentNetworkACL ACLContainer
+	if err := json.Unmarshal([]byte(aclJson), &currentNetworkACL); err != nil {
+		return nil, err
+	}
+	return currentNetworkACL, nil
+}
+
+// fetchACLContainerJson - fetch the current ACL of given container except in json string
+func fetchACLContainerJson(containerID ContainerID) (ACLJson, error) {
+	currentACLs, err := database.FetchRecord(database.NODE_ACLS_TABLE_NAME, string(containerID))
+	if err != nil {
+		return ACLJson(""), err
+	}
+	return ACLJson(currentACLs), nil
+}
+
+// upsertACL - applies a ACL to the db, overwrites or creates
+func upsertACL(containerID ContainerID, ID AclID, acl ACL) (ACL, error) {
+	currentNetACL, err := fetchACLContainer(containerID)
+	if err != nil {
+		return acl, err
+	}
+	currentNetACL[ID] = acl
+	_, err = upsertACLContainer(containerID, currentNetACL)
+	return acl, err
+}
+
+// upsertACLContainer - Inserts or updates a network ACL given the json string of the ACL and the container ID
+// if nil, create it
+func upsertACLContainer(containerID ContainerID, aclContainer ACLContainer) (ACLContainer, error) {
+	if aclContainer == nil {
+		aclContainer = make(ACLContainer)
+	}
+	return aclContainer, database.Insert(string(containerID), string(convertNetworkACLtoACLJson(aclContainer)), database.NODE_ACLS_TABLE_NAME)
+}
+
+func convertNetworkACLtoACLJson(networkACL ACLContainer) ACLJson {
+	data, err := json.Marshal(networkACL)
+	if err != nil {
+		return ""
+	}
+	return ACLJson(data)
+}

+ 87 - 0
logic/acls/nodeacls/modify.go

@@ -0,0 +1,87 @@
+package nodeacls
+
+import (
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logic/acls"
+)
+
+// CreateNodeACL - inserts or updates a node ACL on given network and adds to state
+func CreateNodeACL(networkID NetworkID, nodeID NodeID, defaultVal byte) (acls.ACL, error) {
+	if defaultVal != acls.NotAllowed && defaultVal != acls.Allowed {
+		defaultVal = acls.NotAllowed
+	}
+	var currentNetworkACL, err = FetchAllACLs(networkID)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			currentNetworkACL, err = currentNetworkACL.New(acls.ContainerID(networkID))
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			return nil, err
+		}
+	}
+	var newNodeACL = make(acls.ACL)
+	for existingNodeID := range currentNetworkACL {
+		currentNetworkACL[existingNodeID][acls.AclID(nodeID)] = defaultVal // set the old nodes to default value for new node
+		newNodeACL[existingNodeID] = defaultVal                            // set the old nodes in new node ACL to default value
+	}
+	currentNetworkACL[acls.AclID(nodeID)] = newNodeACL                        // append the new node's ACL
+	retNetworkACL, err := currentNetworkACL.Save(acls.ContainerID(networkID)) // insert into db
+	if err != nil {
+		return nil, err
+	}
+	return retNetworkACL[acls.AclID(nodeID)], nil
+}
+
+// AllowNode - allow access between two nodes in memory
+func AllowNodes(networkID NetworkID, node1, node2 NodeID) (acls.ACLContainer, error) {
+	container, err := FetchAllACLs(networkID)
+	if err != nil {
+		return nil, err
+	}
+	container[acls.AclID(node1)].Allow(acls.AclID(node2))
+	container[acls.AclID(node2)].Allow(acls.AclID(node1))
+	return container, nil
+}
+
+// DisallowNodes - deny access between two nodes
+func DisallowNodes(networkID NetworkID, node1, node2 NodeID) (acls.ACLContainer, error) {
+	container, err := FetchAllACLs(networkID)
+	if err != nil {
+		return nil, err
+	}
+	container[acls.AclID(node1)].Disallow(acls.AclID(node2))
+	container[acls.AclID(node2)].Disallow(acls.AclID(node1))
+	return container, nil
+}
+
+// UpdateNodeACL - updates a node's ACL in state
+func UpdateNodeACL(networkID NetworkID, nodeID NodeID, acl acls.ACL) (acls.ACL, error) {
+	var currentNetworkACL, err = FetchAllACLs(networkID)
+	if err != nil {
+		return nil, err
+	}
+	currentNetworkACL[acls.AclID(nodeID)] = acl
+	return currentNetworkACL[acls.AclID(nodeID)].Save(acls.ContainerID(networkID), acls.AclID(nodeID))
+}
+
+// RemoveNodeACL - removes a specific Node's ACL, returns the NetworkACL and error
+func RemoveNodeACL(networkID NetworkID, nodeID NodeID) (acls.ACLContainer, error) {
+	var currentNetworkACL, err = FetchAllACLs(networkID)
+	if err != nil {
+		return nil, err
+	}
+	for currentNodeID := range currentNetworkACL {
+		if NodeID(currentNodeID) != nodeID {
+			currentNetworkACL[currentNodeID].Remove(acls.AclID(nodeID))
+		}
+	}
+	delete(currentNetworkACL, acls.AclID(nodeID))
+	return currentNetworkACL.Save(acls.ContainerID(networkID))
+}
+
+// DeleteACLContainer - removes an ACLContainer state from db
+func DeleteACLContainer(network NetworkID) error {
+	return database.DeleteRecord(database.NODE_ACLS_TABLE_NAME, string(network))
+}

+ 53 - 0
logic/acls/nodeacls/retrieve.go

@@ -0,0 +1,53 @@
+package nodeacls
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/gravitl/netmaker/logic/acls"
+)
+
+// AreNodesAllowed - checks if nodes are allowed to communicate in their network ACL
+func AreNodesAllowed(networkID NetworkID, node1, node2 NodeID) bool {
+	var currentNetworkACL, err = FetchAllACLs(networkID)
+	if err != nil {
+		return false
+	}
+	return currentNetworkACL[acls.AclID(node1)].IsAllowed(acls.AclID(node2)) && currentNetworkACL[acls.AclID(node2)].IsAllowed(acls.AclID(node1))
+}
+
+// FetchNodeACL - fetches a specific node's ACL in a given network
+func FetchNodeACL(networkID NetworkID, nodeID NodeID) (acls.ACL, error) {
+	var currentNetworkACL, err = FetchAllACLs(networkID)
+	if err != nil {
+		return nil, err
+	}
+	if currentNetworkACL[acls.AclID(nodeID)] == nil {
+		return nil, fmt.Errorf("no node ACL present for node %s", nodeID)
+	}
+	return currentNetworkACL[acls.AclID(nodeID)], nil
+}
+
+// FetchNodeACLJson - fetches a node's acl in given network except returns the json string
+func FetchNodeACLJson(networkID NetworkID, nodeID NodeID) (acls.ACLJson, error) {
+	currentNodeACL, err := FetchNodeACL(networkID, nodeID)
+	if err != nil {
+		return "", err
+	}
+	jsonData, err := json.Marshal(&currentNodeACL)
+	if err != nil {
+		return "", err
+	}
+	return acls.ACLJson(jsonData), nil
+}
+
+// FetchAllACLs - fetchs all node
+func FetchAllACLs(networkID NetworkID) (acls.ACLContainer, error) {
+	var err error
+	var currentNetworkACL acls.ACLContainer
+	currentNetworkACL, err = currentNetworkACL.Get(acls.ContainerID(networkID))
+	if err != nil {
+		return nil, err
+	}
+	return currentNetworkACL, nil
+}

+ 14 - 0
logic/acls/nodeacls/types.go

@@ -0,0 +1,14 @@
+package nodeacls
+
+import (
+	"github.com/gravitl/netmaker/logic/acls"
+)
+
+type (
+	// NodeACL - interface for NodeACLs
+	NodeACL acls.ACL
+	// NodeID - node ID for ACLs
+	NodeID acls.AclID
+	// NetworkID - ACL container based on network ID for nodes
+	NetworkID acls.ContainerID
+)

+ 27 - 0
logic/acls/types.go

@@ -0,0 +1,27 @@
+package acls
+
+var (
+	// NotPresent - 0 - not present (default)
+	NotPresent = byte(0)
+	// NotAllowed - 1 - not allowed access
+	NotAllowed = byte(1) // 1 - not allowed
+	// Allowed - 2 - allowed access
+	Allowed = byte(2)
+)
+
+type (
+	// AclID - the node id of a given node
+	AclID string
+
+	// ACL - the ACL of other nodes in a NetworkACL for a single unique node
+	ACL map[AclID]byte
+
+	// ACLJson - the string representation in JSON of an ACL Node or Network
+	ACLJson string
+
+	// ContainerID - the networkID of a given network
+	ContainerID string
+
+	// ACLContainer - the total list of all node's ACL in a given network
+	ACLContainer map[AclID]ACL
+)

+ 12 - 4
logic/extpeers.go

@@ -33,7 +33,7 @@ func GetExtPeersList(node *models.Node) ([]models.ExtPeersResponse, error) {
 			logger.Log(2, "failed to unmarshal ext client")
 			continue
 		}
-		if extClient.Network == node.Network && extClient.IngressGatewayID == node.ID {
+		if extClient.Enabled && extClient.Network == node.Network && extClient.IngressGatewayID == node.ID {
 			peers = append(peers, peer)
 		}
 	}
@@ -133,6 +133,14 @@ func CreateExtClient(extclient *models.ExtClient) error {
 		extclient.Address = newAddress
 	}
 
+	if extclient.Address6 == "" {
+		addr6, err := UniqueAddress6(extclient.Network)
+		if err != nil {
+			return err
+		}
+		extclient.Address6 = addr6
+	}
+
 	if extclient.ClientID == "" {
 		extclient.ClientID = models.GenerateNodeName()
 	}
@@ -150,18 +158,18 @@ func CreateExtClient(extclient *models.ExtClient) error {
 	if err = database.Insert(key, string(data), database.EXT_CLIENT_TABLE_NAME); err != nil {
 		return err
 	}
-	err = SetNetworkNodesLastModified(extclient.Network)
-	return err
+	return SetNetworkNodesLastModified(extclient.Network)
 }
 
 // UpdateExtClient - only supports name changes right now
-func UpdateExtClient(newclientid string, network string, client *models.ExtClient) (*models.ExtClient, error) {
+func UpdateExtClient(newclientid string, network string, enabled bool, client *models.ExtClient) (*models.ExtClient, error) {
 
 	err := DeleteExtClient(network, client.ClientID)
 	if err != nil {
 		return client, err
 	}
 	client.ClientID = newclientid
+	client.Enabled = enabled
 	CreateExtClient(client)
 	return client, err
 }

+ 6 - 0
logic/networks.go

@@ -12,6 +12,7 @@ import (
 	"github.com/go-playground/validator/v10"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/validation"
@@ -41,6 +42,11 @@ func GetNetworks() ([]models.Network, error) {
 
 // DeleteNetwork - deletes a network
 func DeleteNetwork(network string) error {
+	// remove ACL for network
+	err := nodeacls.DeleteACLContainer(nodeacls.NetworkID(network))
+	if err != nil {
+		logger.Log(1, "failed to remove the node acls during network delete for network,", network)
+	}
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 		// delete server nodes first then db records

+ 44 - 0
logic/nodes.go

@@ -12,6 +12,8 @@ import (
 	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
@@ -174,6 +176,41 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 	return fmt.Errorf("failed to update node " + currentNode.ID + ", cannot change ID.")
 }
 
+// DeleteNodeByID - deletes a node from database or moves into delete nodes table
+func DeleteNodeByID(node *models.Node, exterminate bool) error {
+	var err error
+	var key = node.ID
+	if !exterminate {
+		node.Action = models.NODE_DELETE
+		nodedata, err := json.Marshal(&node)
+		if err != nil {
+			return err
+		}
+		err = database.Insert(key, string(nodedata), database.DELETED_NODES_TABLE_NAME)
+		if err != nil {
+			return err
+		}
+	} else {
+		if err := database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, key); err != nil {
+			logger.Log(2, err.Error())
+		}
+	}
+	if err = database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
+		return err
+	}
+	if servercfg.IsDNSMode() {
+		SetDNS()
+	}
+
+	_, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID))
+	if err != nil {
+		// ignoring for now, could hit a nil pointer if delete called twice
+		logger.Log(2, "attempted to remove node ACL for node", node.Name, node.ID)
+	}
+
+	return removeLocalServer(node)
+}
+
 // IsNodeIDUnique - checks if node id is unique
 func IsNodeIDUnique(node *models.Node) (bool, error) {
 	_, err := database.FetchRecord(database.NODES_TABLE_NAME, node.ID)
@@ -274,6 +311,13 @@ func CreateNode(node *models.Node) error {
 	if err != nil {
 		return err
 	}
+	// TODO get template logic to decide initial ACL value
+	_, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), acls.Allowed)
+	if err != nil {
+		logger.Log(1, "failed to create node ACL for node,", node.ID, "err:", err.Error())
+		return err
+	}
+
 	if node.IsPending != "yes" {
 		DecrimentKey(node.Network, node.AccessKey)
 	}

+ 19 - 11
logic/peers.go

@@ -1,15 +1,17 @@
 package logic
 
 import (
+	"fmt"
 	"log"
 	"net"
-	"os"
 	"strconv"
 	"strings"
 	"time"
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -33,7 +35,7 @@ func GetHubPeer(networkName string) []models.Node {
 */
 
 // GetNodePeers - fetches peers for a given node
-func GetNodePeers(networkName string, excludeRelayed bool, isP2S bool) ([]models.Node, error) {
+func GetNodePeers(networkName, nodeid string, excludeRelayed bool, isP2S bool) ([]models.Node, error) {
 	var peers []models.Node
 	var networkNodes, egressNetworkNodes, err = getNetworkEgressAndNodes(networkName)
 	if err != nil {
@@ -45,7 +47,16 @@ func GetNodePeers(networkName string, excludeRelayed bool, isP2S bool) ([]models
 		logger.Log(2, errN.Error())
 	}
 
+	currentNetworkACLs, aclErr := nodeacls.FetchAllACLs(nodeacls.NetworkID(networkName))
+	if aclErr != nil {
+		return peers, aclErr
+	}
+
 	for _, node := range networkNodes {
+		if !currentNetworkACLs.IsAllowed(acls.AclID(nodeid), acls.AclID(node.ID)) {
+			continue
+		}
+
 		var peer = models.Node{}
 		if node.IsEgressGateway == "yes" { // handle egress stuff
 			peer.EgressGatewayRanges = node.EgressGatewayRanges
@@ -79,7 +90,7 @@ func GetNodePeers(networkName string, excludeRelayed bool, isP2S bool) ([]models
 					}
 				}
 			}
-			if !isP2S || peer.IsHub == "yes" {
+			if (!isP2S || peer.IsHub == "yes") && currentNetworkACLs.IsAllowed(acls.AclID(nodeid), acls.AclID(node.ID)) {
 				peers = append(peers, peer)
 			}
 		}
@@ -107,7 +118,7 @@ func GetPeersList(refnode *models.Node) ([]models.Node, error) {
 		isP2S = true
 	}
 	if relayedNodeAddr == "" {
-		peers, err = GetNodePeers(networkName, excludeRelayed, isP2S)
+		peers, err = GetNodePeers(networkName, refnode.ID, excludeRelayed, isP2S)
 	} else {
 		var relayNode models.Node
 		relayNode, err = GetNodeRelay(networkName, relayedNodeAddr)
@@ -127,7 +138,7 @@ func GetPeersList(refnode *models.Node) ([]models.Node, error) {
 			} else {
 				peerNode.AllowedIPs = append(peerNode.AllowedIPs, peerNode.RelayAddrs...)
 			}
-			nodepeers, err := GetNodePeers(networkName, false, isP2S)
+			nodepeers, err := GetNodePeers(networkName, refnode.ID, false, isP2S)
 			if err == nil && peerNode.UDPHolePunch == "yes" {
 				for _, nodepeer := range nodepeers {
 					if nodepeer.Address == peerNode.Address {
@@ -165,11 +176,13 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 	// #1 Set Keepalive values: set_keepalive
 	// #2 Set local address: set_local - could be a LOT BETTER and fix some bugs with additional logic
 	// #3 Set allowedips: set_allowedips
+	var dns string
 	for _, peer := range currentPeers {
 		if peer.ID == node.ID {
 			//skip yourself
 			continue
 		}
+		dns = dns + fmt.Sprintf("%s %s.%s\n", peer.Address, peer.Name, peer.Network)
 		pubkey, err := wgtypes.ParseKey(peer.PublicKey)
 		if err != nil {
 			return models.PeerUpdate{}, err
@@ -225,12 +238,7 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 
 
 	*/
-	dns, err := os.ReadFile("./config/dnsconfig/netmaker.hosts")
-	if err != nil {
-		logger.Log(0, "failed to read netmaker.hosts", err.Error())
-	} else {
-		peerUpdate.DNS = dns
-	}
+	peerUpdate.DNS = dns
 	return peerUpdate, nil
 }
 

+ 11 - 0
logic/server.go

@@ -11,6 +11,8 @@ import (
 	"time"
 
 	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
@@ -208,6 +210,11 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
 		return nil, hasGateway, gateways, err
 	}
 
+	currentNetworkACL, err := nodeacls.FetchAllACLs(nodeacls.NetworkID(serverNode.Network))
+	if err != nil {
+		logger.Log(1, "could not fetch current ACL list, proceeding with all peers")
+	}
+
 	for _, node := range nodes {
 		pubkey, err := wgtypes.ParseKey(node.PublicKey)
 		if err != nil {
@@ -225,6 +232,9 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
 				continue
 			}
 		}
+		if currentNetworkACL != nil && currentNetworkACL.IsAllowed(acls.AclID(serverNode.ID), acls.AclID(node.ID)) {
+			continue
+		}
 
 		var peer wgtypes.PeerConfig
 		var peeraddr = net.IPNet{
@@ -290,6 +300,7 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
 			ReplaceAllowedIPs:           true,
 			AllowedIPs:                  allowedips,
 		}
+
 		peers = append(peers, peer)
 	}
 	if serverNode.IsIngressGateway == "yes" {

+ 1 - 29
logic/util.go

@@ -17,7 +17,6 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/servercfg"
 )
 
 // IsBase64 - checks if a string is in base64 format
@@ -101,34 +100,6 @@ func GenerateCryptoString(n int) (string, error) {
 	return string(ret), nil
 }
 
-// DeleteNodeByID - deletes a node from database or moves into delete nodes table
-func DeleteNodeByID(node *models.Node, exterminate bool) error {
-	var err error
-	var key = node.ID
-	if !exterminate {
-		node.Action = models.NODE_DELETE
-		nodedata, err := json.Marshal(&node)
-		if err != nil {
-			return err
-		}
-		err = database.Insert(key, string(nodedata), database.DELETED_NODES_TABLE_NAME)
-		if err != nil {
-			return err
-		}
-	} else {
-		if err := database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, key); err != nil {
-			logger.Log(2, err.Error())
-		}
-	}
-	if err = database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
-		return err
-	}
-	if servercfg.IsDNSMode() {
-		SetDNS()
-	}
-	return removeLocalServer(node)
-}
-
 // RandomString - returns a random string in a charset
 func RandomString(length int) string {
 	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@@ -181,6 +152,7 @@ func setPeerInfo(node *models.Node) models.Node {
 	peer.PublicKey = node.PublicKey
 	peer.Endpoint = node.Endpoint
 	peer.Name = node.Name
+	peer.Network = node.Network
 	peer.LocalAddress = node.LocalAddress
 	peer.ListenPort = node.ListenPort
 	peer.AllowedIPs = node.AllowedIPs

+ 5 - 0
main.go

@@ -66,6 +66,11 @@ func initialize() { // Client Mode Prereq Check
 		logger.Log(0, "no OAuth provider found or not configured, continuing without OAuth")
 	}
 
+	err = serverctl.SetDefaultACLS()
+	if err != nil {
+		logger.FatalLog("error setting default acls: ", err.Error())
+	}
+
 	if servercfg.IsClientMode() != "off" {
 		output, err := ncutils.RunCmd("id -u", true)
 		if err != nil {

+ 2 - 0
models/extclient.go

@@ -8,7 +8,9 @@ type ExtClient struct {
 	PublicKey              string `json:"publickey" bson:"publickey"`
 	Network                string `json:"network" bson:"network"`
 	Address                string `json:"address" bson:"address"`
+	Address6               string `json:"address6" bson:"address6"`
 	IngressGatewayID       string `json:"ingressgatewayid" bson:"ingressgatewayid"`
 	IngressGatewayEndpoint string `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"`
 	LastModified           int64  `json:"lastmodified" bson:"lastmodified"`
+	Enabled                bool   `json:"enabled" bson:"enabled"`
 }

+ 1 - 1
models/mqtt.go

@@ -7,7 +7,7 @@ type PeerUpdate struct {
 	Network     string               `json:"network" bson:"network" yaml:"network"`
 	ServerAddrs []ServerAddr         `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
 	Peers       []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
-	DNS         []byte               `json:"dns" bson:'dns" yaml:"dns"`
+	DNS         string               `json:"dns" bson:"dns" yaml:"dns"`
 }
 
 // KeyUpdate - key update struct

+ 15 - 0
netclient/build/netclient.service

@@ -0,0 +1,15 @@
+[Unit]
+Description=Netclient Daemon
+Documentation=https://docs.netmaker.org https://k8s.netmaker.org
+After=network-online.target
+Wants=network-online.target systemd-networkd-wait-online.service
+
+[Service]
+User=root
+Type=simple
+ExecStart=/sbin/netclient daemon
+Restart=on-failure
+RestartSec=15s
+
+[Install]
+WantedBy=multi-user.target

+ 14 - 22
netclient/functions/mqhandlers.go

@@ -3,13 +3,11 @@ package functions
 import (
 	"encoding/json"
 	"fmt"
-	"os"
 	"runtime"
 	"strings"
 	"time"
 
 	mqtt "github.com/eclipse/paho.mqtt.golang"
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/config"
 	"github.com/gravitl/netmaker/netclient/local"
@@ -143,7 +141,7 @@ func NodeUpdate(client mqtt.Client, msg mqtt.Message) {
 	//deal with DNS
 	if newNode.DNSOn != "yes" && shouldDNSChange && nodeCfg.Node.Interface != "" {
 		ncutils.Log("settng DNS off")
-		if err := removeHostDNS(ncutils.IsWindows()); err != nil {
+		if err := removeHostDNS(nodeCfg.Network, ncutils.IsWindows()); err != nil {
 			ncutils.Log("error removing netmaker profile from /etc/hosts " + err.Error())
 		}
 		//		_, err := ncutils.RunCmd("/usr/bin/resolvectl revert "+nodeCfg.Node.Interface, true)
@@ -197,36 +195,30 @@ func UpdatePeers(client mqtt.Client, msg mqtt.Message) {
 		ncutils.Log("error syncing wg after peer update: " + err.Error())
 		return
 	}
-	logger.Log(0, "DNS updating /etc/hosts")
+	ncutils.Log("received peer update for node " + cfg.Node.Name + " " + cfg.Node.Network)
+	//skip dns updates if this is a peer update for comms network
+	if cfg.Node.NetworkSettings.IsComms == "yes" {
+		return
+	}
 	if cfg.Node.DNSOn == "yes" {
-		if err := setHostDNS(peerUpdate.DNS, ncutils.IsWindows()); err != nil {
+		if err := setHostDNS(peerUpdate.DNS, cfg.Node.Network, ncutils.IsWindows()); err != nil {
 			ncutils.Log("error updating /etc/hosts " + err.Error())
 			return
 		}
 	} else {
-		if err := removeHostDNS(ncutils.IsWindows()); err != nil {
-			ncutils.Log("error removing netmaker profile from /etc/hosts " + err.Error())
+		if err := removeHostDNS(cfg.Node.Network, ncutils.IsWindows()); err != nil {
+			ncutils.Log("error removing profile from /etc/hosts " + err.Error())
 			return
 		}
 	}
 }
 
-func setHostDNS(dns []byte, windows bool) error {
+func setHostDNS(dns, network string, windows bool) error {
 	etchosts := "/etc/hosts"
 	if windows {
 		etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"
 	}
-	tmpfile := "/tmp/dnsdata"
-	if windows {
-		tmpfile = "c:\\windows\\temp\\dnsdata"
-	}
-	if err := os.WriteFile(tmpfile, dns, 0600); err != nil {
-		return err
-	}
-	dnsdata, err := os.Open(tmpfile)
-	if err != nil {
-		return err
-	}
+	dnsdata := strings.NewReader(dns)
 	profile, err := parser.ParseProfile(dnsdata)
 	if err != nil {
 		return err
@@ -235,7 +227,7 @@ func setHostDNS(dns []byte, windows bool) error {
 	if err != nil {
 		return err
 	}
-	profile.Name = "netmaker"
+	profile.Name = network
 	profile.Status = types.Enabled
 	if err := hosts.ReplaceProfile(profile); err != nil {
 		return err
@@ -246,7 +238,7 @@ func setHostDNS(dns []byte, windows bool) error {
 	return nil
 }
 
-func removeHostDNS(windows bool) error {
+func removeHostDNS(network string, windows bool) error {
 	etchosts := "/etc/hosts"
 	if windows {
 		etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"
@@ -255,7 +247,7 @@ func removeHostDNS(windows bool) error {
 	if err != nil {
 		return err
 	}
-	if err := hosts.RemoveProfile("netmaker"); err != nil {
+	if err := hosts.RemoveProfile(network); err != nil {
 		return err
 	}
 	if err := hosts.Flush(); err != nil {

+ 1 - 1
netclient/netclient.exe.manifest.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
     <assemblyIdentity
-            version="0.11.1.0"
+            version="0.12.0.0"
             processorArchitecture="*"
             name="netclient.exe"
             type="win32"

+ 0 - 0
netclient/windowsdata/resource/netclient.syso → netclient/netclient.syso


+ 1 - 1
netclient/versioninfo.json

@@ -29,7 +29,7 @@
         "OriginalFilename": "",
         "PrivateBuild": "",
         "ProductName": "Netclient",
-        "ProductVersion": "v0.11.1.0",
+        "ProductVersion": "v0.12.0.0",
         "SpecialBuild": ""
     },
     "VarFileInfo": {

+ 28 - 15
netclient/wireguard/common.go

@@ -166,21 +166,7 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 		}
 	}
 	// ensure you clear any existing interface first
-	d, _ := wgclient.Device(deviceiface)
-	startTime := time.Now()
-	for d != nil && d.Name == deviceiface {
-		if err = RemoveConf(deviceiface, false); err != nil { // remove interface first
-			if strings.Contains(err.Error(), "does not exist") {
-				err = nil
-				break
-			}
-		}
-		time.Sleep(time.Second >> 2)
-		d, _ = wgclient.Device(deviceiface)
-		if time.Now().After(startTime.Add(time.Second << 4)) {
-			break
-		}
-	}
+	RemoveConfGraceful(deviceiface)
 	ApplyConf(node, ifacename, confPath)            // Apply initially
 	ncutils.PrintLog("waiting for interface...", 1) // ensure interface is created
 	output, _ := ncutils.RunCmd("wg", false)
@@ -301,6 +287,7 @@ func ApplyConf(node *models.Node, ifacename string, confPath string) error {
 	var err error
 	switch os {
 	case "windows":
+		RemoveConfGraceful(ifacename)
 		ApplyWindowsConf(confPath)
 	case "darwin":
 		ApplyMacOSConf(node, ifacename, confPath)
@@ -478,3 +465,29 @@ func UpdatePrivateKey(file, privateKey string) error {
 	}
 	return nil
 }
+
+// RemoveConfGraceful - Run remove conf and wait for it to actually be gone before proceeding
+func RemoveConfGraceful(ifacename string) {
+	// ensure you clear any existing interface first
+	wgclient, err := wgctrl.New()
+	if err != nil {
+		ncutils.PrintLog("could not create wgclient", 0)
+		return
+	}
+	defer wgclient.Close()
+	d, _ := wgclient.Device(ifacename)
+	startTime := time.Now()
+	for d != nil && d.Name == ifacename {
+		if err = RemoveConf(ifacename, false); err != nil { // remove interface first
+			if strings.Contains(err.Error(), "does not exist") {
+				err = nil
+				break
+			}
+		}
+		time.Sleep(time.Second >> 2)
+		d, _ = wgclient.Device(ifacename)
+		if time.Now().After(startTime.Add(time.Second << 4)) {
+			break
+		}
+	}
+}

+ 5 - 6
netclient/wireguard/noquick.go

@@ -7,7 +7,6 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/config"
 	"github.com/gravitl/netmaker/netclient/ncutils"
@@ -72,7 +71,7 @@ func ApplyWithoutWGQuick(node *models.Node, ifacename string, confPath string) e
 		}
 	}
 	if _, err := ncutils.RunCmd(ipExec+" link set down dev "+ifacename, false); err != nil {
-		logger.Log(2, "attempted to remove interface before editing")
+		ncutils.PrintLog("attempted to remove interface before editing", 1)
 		return err
 	}
 	if node.PostDown != "" {
@@ -81,7 +80,7 @@ func ApplyWithoutWGQuick(node *models.Node, ifacename string, confPath string) e
 	}
 	// set MTU of node interface
 	if _, err := ncutils.RunCmd(ipExec+" link set mtu "+strconv.Itoa(int(node.MTU))+" up dev "+ifacename, true); err != nil {
-		logger.Log(2, "failed to create interface with mtu", strconv.Itoa(int(node.MTU)), "-", ifacename)
+		ncutils.PrintLog("failed to create interface with mtu "+strconv.Itoa(int(node.MTU))+"-"+ifacename, 1)
 		return err
 	}
 	if node.PostUp != "" {
@@ -89,7 +88,7 @@ func ApplyWithoutWGQuick(node *models.Node, ifacename string, confPath string) e
 		_ = ncutils.RunCmds(runcmds, true)
 	}
 	if node.Address6 != "" && node.IsDualStack == "yes" {
-		logger.Log(1, "adding address:", node.Address6)
+		ncutils.PrintLog("adding address: "+node.Address6, 1)
 		_, _ = ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+node.Address6+"/64", true)
 	}
 	return nil
@@ -104,8 +103,8 @@ func RemoveWithoutWGQuick(ifacename string) error {
 	out, err := ncutils.RunCmd(ipExec+" link del "+ifacename, false)
 	dontprint := strings.Contains(out, "does not exist") || strings.Contains(out, "Cannot find device")
 	if err != nil && !dontprint {
-		logger.Log(1, "error running command:", ipExec, "link del", ifacename)
-		logger.Log(1, out)
+		ncutils.PrintLog("error running command: "+ipExec+" link del "+ifacename, 1)
+		ncutils.PrintLog(out, 1)
 	}
 	network := strings.ReplaceAll(ifacename, "nm-", "")
 	nodeconf, err := config.ReadConfig(network)

BIN
netmaker.png


+ 20 - 0
serverctl/serverctl.go

@@ -10,6 +10,8 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
@@ -144,3 +146,21 @@ func SyncServerNetwork(network string) error {
 	*/
 	return nil
 }
+
+// SetDefaultACLS - runs through each network to see if ACL's are set. If not, goes through each node in network and adds the default ACL
+func SetDefaultACLS() error {
+	// upgraded systems will not have ACL's set, which is why we need this function
+	nodes, err := logic.GetAllNodes()
+	if err != nil {
+		return err
+	}
+	for i := range nodes {
+		currentNodeACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID))
+		if (err != nil && (database.IsEmptyRecord(err) || strings.Contains(err.Error(), "no node ACL present"))) || currentNodeACL == nil {
+			if _, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID), acls.Allowed); err != nil {
+				logger.Log(1, "could not create a default ACL for node", nodes[i].ID)
+			}
+		}
+	}
+	return nil
+}