32 Commits

Author SHA1 Message Date
github-actions[bot]
87311cac9c update README.md 2025-10-23 05:06:45 +00:00
ElevenNotes
e884b251b6 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-10-23 07:05:21 +02:00
ElevenNotes
ba63b3ae11 docs: add more examples for setting KMS server 2025-10-23 07:05:16 +02:00
ElevenNotes
559803f9d5 chore: upgrade to latest workflow 2025-10-23 07:04:56 +02:00
github-actions[bot]
c1b24dfcca auto update README.md 2025-07-20 21:53:17 +00:00
ElevenNotes
ee192d6d81 fix: wrong image (then test new readme action) 2025-07-20 23:52:08 +02:00
github-actions[bot]
16e90146a4 auto update README.md 2025-07-20 11:10:48 +00:00
ElevenNotes
aced02117a feat: upgrade to latest workflow 2025-07-18 11:12:07 +02:00
ElevenNotes
018a0c38d1 feat: add proper frontend network 2025-07-18 11:11:55 +02:00
github-actions[bot]
13638d92bc github-actions[bot]: update README.md 2025-07-10 05:54:05 +00:00
github-actions[bot]
302e3765b7 [upgrade] 1.0.3 2025-07-10 05:49:02 +00:00
github-actions[bot]
16ec64e4ed github-actions[bot]: update README.md 2025-07-10 05:27:25 +00:00
github-actions[bot]
b02cacc8cb [upgrade] 1.0.1 2025-07-10 05:22:12 +00:00
ElevenNotes
efbc374fdf Merge branch 'master' of https://github.com/11notes/docker-kms 2025-07-09 21:39:48 +02:00
ElevenNotes
2fe67967b0 [upgrade] to latest workflow 2025-07-09 21:39:39 +02:00
github-actions[bot]
7fe09c3a65 github-actions[bot]: update README.md 2025-07-09 19:38:36 +00:00
ElevenNotes
abf93ebf36 [upgrade] latest workflows 2025-07-09 21:32:31 +02:00
ElevenNotes
c7ceef1895 [upgrade] 1.0.3 2025-07-09 21:32:18 +02:00
ElevenNotes
79e9f980dd [fix] refactor and better health check 2025-07-09 21:32:00 +02:00
ElevenNotes
75e540239a [upgrade] 1.0.3 2025-07-09 21:31:45 +02:00
ElevenNotes
3b9fdb0518 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-07-09 20:54:14 +02:00
ElevenNotes
8744c5a656 [feature] latest version 2025-07-09 20:53:37 +02:00
github-actions[bot]
d5643d374d github-actions[bot]: update README.md 2025-06-12 05:23:08 +00:00
github-actions[bot]
febdc20df2 [upgrade] 1.0.1 2025-06-12 05:18:49 +00:00
github-actions[bot]
a3c4b0ccbf github-actions[bot]: update README.md 2025-06-11 07:30:03 +00:00
ElevenNotes
f8ec600025 [feature] new style 2025-06-11 08:57:07 +02:00
ElevenNotes
24a9b2f00e [upgrade] latest workflow 2025-06-11 08:56:06 +02:00
ElevenNotes
2e5987e07e [upgrade] 1.0.2 2025-06-11 08:55:54 +02:00
ElevenNotes
6174e7f2e3 [fix] allow IPv6 2025-06-11 08:55:34 +02:00
ElevenNotes
bde8202670 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-21 08:53:08 +02:00
ElevenNotes
0e8ba02ebc [cut] KMS_LOGLEVEL and KMS_CLIENTCOUNT 2025-05-21 08:52:58 +02:00
github-actions[bot]
0a8b7acd55 github-actions[bot]: update README.md 2025-05-21 06:48:52 +00:00
10 changed files with 629 additions and 226 deletions

View File

@@ -94,7 +94,7 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add .json
git commit -m "[upgrade] ${{ env.LATEST_VERSION }}"
git commit -m "chore: auto upgrade to ${{ env.LATEST_VERSION }}"
git push origin HEAD:master
- name: cron-update / tag

70
.github/workflows/cve.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
name: cve
on:
workflow_dispatch:
schedule:
- cron: "30 15 */2 * *"
jobs:
cve:
runs-on: ubuntu-latest
steps:
- name: init / checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
ref: ${{ github.ref_name }}
fetch-depth: 0
- name: init / setup environment
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
with:
script: |
const { existsSync, readFileSync } = require('node:fs');
const { resolve } = require('node:path');
const { inspect } = require('node:util');
const { Buffer } = require('node:buffer');
const inputs = `${{ toJSON(github.event.inputs) }}`;
const opt = {input:{}, dot:{}};
try{
if(inputs.length > 0){
opt.input = JSON.parse(inputs);
if(opt.input?.etc){
opt.input.etc = JSON.parse(Buffer.from(opt.input.etc, 'base64').toString('ascii'));
}
}
}catch(e){
core.warning('could not parse github.event.inputs');
}
try{
const path = resolve('.json');
if(existsSync(path)){
try{
opt.dot = JSON.parse(readFileSync(path).toString());
}catch(e){
throw new Error('could not parse .json');
}
}else{
throw new Error('.json does not exist');
}
}catch(e){
core.setFailed(e);
}
core.info(inspect(opt, {showHidden:false, depth:null, colors:true}));
core.exportVariable('WORKFLOW_IMAGE', `${opt.dot.image}:${(opt.dot?.semver?.version === undefined) ? 'rolling' : opt.dot.semver.version}`);
core.exportVariable('WORKFLOW_GRYPE_SEVERITY_CUTOFF', (opt.dot?.grype?.severity || 'high'));
- name: grype / scan
id: grype
uses: anchore/scan-action@dc6246fcaf83ae86fcc6010b9824c30d7320729e
with:
image: ${{ env.WORKFLOW_IMAGE }}
fail-build: true
severity-cutoff: ${{ env.WORKFLOW_GRYPE_SEVERITY_CUTOFF }}
output-format: 'sarif'
by-cve: true
cache-db: true

View File

@@ -10,11 +10,11 @@ on:
required: false
default: 'docker'
runs-on:
description: 'set runs-on for workflow (github or selfhosted)'
platform:
description: 'list of platforms to build for'
type: string
required: false
default: 'ubuntu-22.04'
default: "amd64,arm64,arm/v7"
build:
description: 'set WORKFLOW_BUILD'
@@ -30,14 +30,100 @@ on:
description: 'set WORKFLOW_GITHUB_README'
required: false
default: 'false'
etc:
description: 'base64 encoded json string'
required: false
jobs:
# ╔═════════════════════════════════════════════════════╗
# ║ ║
# ║ ║
# ║ CREATE PLATFORM MATRIX ║
# ║ ║
# ║ ║
# ╚═════════════════════════════════════════════════════╝
matrix:
name: create job matrix
runs-on: ubuntu-latest
outputs:
stringify: ${{ steps.setup-matrix.outputs.stringify }}
steps:
# CHECKOUT REPOSITORY
- name: init / checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.ref_name }}
- name: matrix / setup list
id: setup-matrix
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const { existsSync, readFileSync } = require('node:fs');
const { inspect } = require('node:util');
const { resolve } = require('node:path');
const opt = {dot:{}};
try{
const path = resolve('.json');
if(existsSync(path)){
try{
opt.dot = JSON.parse(readFileSync(path).toString());
}catch(e){
throw new Error('could not parse .json');
}
}else{
throw new Error('.json does not exist');
}
}catch(e){
core.setFailed(e);
}
const platforms = (
("${{ github.event.inputs.platform }}" != "amd64,arm64,arm/v7") ? "${{ github.event.inputs.platform }}".split(",") : (
(opt.dot?.platform) ? opt.dot.platform.split(",") : "${{ github.event.inputs.platform }}".split(",")
)
);
const matrix = {include:[]};
for(const platform of platforms){
switch(platform){
case "amd64": matrix.include.push({platform:platform, runner:"ubuntu-24.04"}); break;
case "arm64": matrix.include.push({platform:platform, runner:"ubuntu-24.04-arm"}); break;
case "arm/v7": matrix.include.push({platform:platform, runner:"ubuntu-24.04-arm"}); break;
}
}
const stringify = JSON.stringify(matrix);
core.setOutput('stringify', stringify);
// print
core.info(inspect({opt:opt, matrix:matrix, platforms:platforms}, {showHidden:false, depth:null, colors:true}));
# ╔═════════════════════════════════════════════════════╗
# ║ ║
# ║ ║
# ║ BUILD CONTAINER IMAGE ║
# ║ ║
# ║ ║
# ╚═════════════════════════════════════════════════════╝
docker:
runs-on: ${{ inputs.runs-on }}
name: create container image
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.matrix.outputs.stringify) }}
outputs:
DOCKER_IMAGE_NAME: ${{ steps.setup-environment.outputs.DOCKER_IMAGE_NAME }}
DOCKER_IMAGE_MERGE_TAGS: ${{ steps.setup-environment.outputs.DOCKER_IMAGE_MERGE_TAGS }}
DOCKER_IMAGE_DESCRIPTION: ${{ steps.setup-environment.outputs.DOCKER_IMAGE_DESCRIPTION }}
DOCKER_IMAGE_NAME_AND_VERSION: ${{ steps.setup-environment.outputs.DOCKER_IMAGE_NAME_AND_VERSION }}
DOCKER_IMAGE_ARGUMENTS: ${{ steps.setup-environment.outputs.DOCKER_IMAGE_ARGUMENTS }}
WORKFLOW_BUILD: ${{ steps.setup-environment.outputs.WORKFLOW_BUILD }}
timeout-minutes: 1440
services:
@@ -47,27 +133,39 @@ jobs:
- 5000:5000
permissions:
actions: read
actions: write
contents: write
packages: write
attestations: write
id-token: write
security-events: write
steps:
needs: matrix
steps:
# ╔═════════════════════════════════════════════════════╗
# ║ SETUP ENVIRONMENT ║
# ╚═════════════════════════════════════════════════════╝
# CHECKOUT ALL DEPTHS (ALL TAGS)
- name: init / checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.ref_name }}
fetch-depth: 0
# SETUP ENVIRONMENT VARIABLES AND INPUTS
- name: init / setup environment
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
id: setup-environment
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const { existsSync, readFileSync } = require('node:fs');
const { resolve } = require('node:path');
const { inspect } = require('node:util');
const { Buffer } = require('node:buffer');
const inputs = `${{ toJSON(github.event.inputs) }}`;
const opt = {input:{}, dot:{}};
const inputs = `${{ toJSON(github.event.inputs) }}`.
replace(/"platform":\s*"\[(.+)\]",/i, `"platform": [$1],`);
const opt = {input:{}, dot:{}};
try{
if(inputs.length > 0){
@@ -78,6 +176,7 @@ jobs:
}
}catch(e){
core.warning('could not parse github.event.inputs');
core.warning(inputs);
}
try{
@@ -95,16 +194,18 @@ jobs:
core.setFailed(e);
}
core.info(inspect(opt, {showHidden:false, depth:null, colors:true}));
const docker = {
image:{
name:opt.dot.image,
arch:(opt.dot.arch || 'linux/amd64,linux/arm64'),
arch:(opt.input?.etc?.arch || opt.dot?.arch || 'linux/amd64,linux/arm64'),
prefix:((opt.input?.etc?.semverprefix) ? `${opt.input?.etc?.semverprefix}-` : ''),
suffix:((opt.input?.etc?.semversuffix) ? `-${opt.input?.etc?.semversuffix}` : ''),
description:(opt.dot?.readme?.description || ''),
platform:{
sanitized:"${{ matrix.platform }}".replace(/[^A-Z-a-z0-9]+/i, ""),
},
tags:[],
build:(opt.input?.build === undefined) ? false : opt.input.build,
},
app:{
image:opt.dot.image,
@@ -117,8 +218,10 @@ jobs:
},
cache:{
registry:'localhost:5000/',
enable:(opt.input?.etc?.cache === undefined) ? true : opt.input.etc.cache,
},
tags:[],
merge_tags:[],
};
docker.cache.name = `${docker.image.name}:${docker.image.prefix}buildcache${docker.image.suffix}`;
@@ -126,18 +229,22 @@ jobs:
docker.app.prefix = docker.image.prefix;
docker.app.suffix = docker.image.suffix;
const semver = docker.app.version.split('.');
// setup tags
if(!opt.dot?.semver?.disable?.rolling){
if(!opt.dot?.semver?.disable?.rolling && !opt.input.etc?.semver?.disable?.rolling){
docker.image.tags.push('rolling');
}
if(opt.input?.etc?.dockerfile !== 'arch.dockerfile' && opt.input?.etc?.tag){
docker.image.tags.push(`${context.sha.substring(0,7)}`);
docker.image.tags.push(opt.input.etc.tag);
docker.image.tags.push(`${opt.input.etc.tag}-${docker.app.version}`);
if(Array.isArray(semver)){
if(semver.length >= 1) docker.image.tags.push(`${opt.input.etc.tag}-${semver[0]}`);
if(semver.length >= 2) docker.image.tags.push(`${opt.input.etc.tag}-${semver[0]}.${semver[1]}`);
if(semver.length >= 3) docker.image.tags.push(`${opt.input.etc.tag}-${semver[0]}.${semver[1]}.${semver[2]}`);
}else{
docker.image.tags.push(`${opt.input.etc.tag}-${docker.app.version}`);
}
docker.cache.name = `${docker.image.name}:buildcache-${opt.input.etc.tag}`;
}else{
const semver = docker.app.version.split('.');
docker.image.tags.push(`${context.sha.substring(0,7)}`);
}else if(docker.app.version !== 'latest'){
if(Array.isArray(semver)){
if(semver.length >= 1) docker.image.tags.push(`${semver[0]}`);
if(semver.length >= 2) docker.image.tags.push(`${semver[0]}.${semver[1]}`);
@@ -145,12 +252,15 @@ jobs:
}
if(opt.dot?.semver?.stable && new RegExp(opt.dot?.semver.stable, 'ig').test(docker.image.tags.join(','))) docker.image.tags.push('stable');
if(opt.dot?.semver?.latest && new RegExp(opt.dot?.semver.latest, 'ig').test(docker.image.tags.join(','))) docker.image.tags.push('latest');
}else{
docker.image.tags.push('latest');
}
for(const tag of docker.image.tags){
docker.tags.push(`${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}`);
docker.tags.push(`ghcr.io/${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}`);
docker.tags.push(`quay.io/${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}`);
docker.tags.push(`${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}-${docker.image.platform.sanitized}`);
docker.tags.push(`ghcr.io/${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}-${docker.image.platform.sanitized}`);
docker.tags.push(`quay.io/${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}-${docker.image.platform.sanitized}`);
docker.merge_tags.push(`${docker.image.prefix}${tag}${docker.image.suffix}`);
}
// setup build arguments
@@ -171,69 +281,92 @@ jobs:
// export to environment
core.exportVariable('DOCKER_CACHE_REGISTRY', docker.cache.registry);
core.exportVariable('DOCKER_CACHE_NAME', docker.cache.name);
core.exportVariable('DOCKER_CACHE_NAME', `${docker.cache.name}-${docker.image.platform.sanitized}`);
core.exportVariable('DOCKER_CACHE_GRYPE', docker.cache.grype);
core.exportVariable('DOCKER_IMAGE_NAME', docker.image.name);
core.exportVariable('DOCKER_IMAGE_ARCH', docker.image.arch);
core.setOutput('DOCKER_IMAGE_NAME', docker.image.name);
core.exportVariable('DOCKER_IMAGE_TAGS', docker.tags.join(','));
core.exportVariable('DOCKER_IMAGE_MERGE_TAGS', docker.merge_tags.join("\r\n"));
core.setOutput('DOCKER_IMAGE_MERGE_TAGS', docker.merge_tags.join("\r\n"));
core.exportVariable('DOCKER_IMAGE_DESCRIPTION', docker.image.description);
core.setOutput('DOCKER_IMAGE_DESCRIPTION', docker.image.description);
core.exportVariable('DOCKER_IMAGE_ARGUMENTS', arguments.join("\r\n"));
core.setOutput('DOCKER_IMAGE_ARGUMENTS', arguments.join("\r\n"));
core.exportVariable('DOCKER_IMAGE_DOCKERFILE', opt.input?.etc?.dockerfile || 'arch.dockerfile');
core.exportVariable('DOCKER_IMAGE_PLATFORM_SANITIZED', docker.image.platform.sanitized);
core.exportVariable('DOCKER_IMAGE_NAME_AND_VERSION', `${docker.image.name}:${docker.app.version}`);
core.setOutput('DOCKER_IMAGE_NAME_AND_VERSION', `${docker.image.name}:${docker.app.version}`);
core.exportVariable('WORKFLOW_BUILD', docker.image.build);
core.setOutput('WORKFLOW_BUILD', docker.image.build);
core.exportVariable('WORKFLOW_BUILD_NO_CACHE', !docker.cache.enable);
core.exportVariable('WORKFLOW_BUILD', (opt.input?.build === undefined) ? false : opt.input.build);
core.exportVariable('WORKFLOW_CREATE_RELEASE', (opt.input?.release === undefined) ? false : opt.input.release);
core.exportVariable('WORKFLOW_CREATE_README', (opt.input?.readme === undefined) ? false : opt.input.readme);
core.exportVariable('WORKFLOW_GRYPE_FAIL_ON_SEVERITY', (opt.dot?.grype?.fail === undefined) ? true : opt.dot.grype.fail);
core.exportVariable('WORKFLOW_GRYPE_SEVERITY_CUTOFF', (opt.dot?.grype?.severity || 'high'));
if(opt.dot?.readme?.comparison){
core.exportVariable('WORKFLOW_CREATE_COMPARISON', true);
core.exportVariable('WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE', opt.dot.readme.comparison.image);
core.exportVariable('WORKFLOW_CREATE_COMPARISON_IMAGE', `${docker.image.name}:${docker.app.version}`);
}
core.exportVariable('WORKFLOW_GRYPE_SEVERITY_CUTOFF', (opt.dot?.grype?.severity || 'critical'));
// print
core.info(inspect({opt:opt, docker:docker}, {showHidden:false, depth:null, colors:true}));
# DOCKER
# ╔═════════════════════════════════════════════════════╗
# ║ CONTAINER REGISTRY LOGIN ║
# ╚═════════════════════════════════════════════════════╝
# DOCKER HUB
- name: docker / login to hub
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
username: 11notes
password: ${{ secrets.DOCKER_TOKEN }}
# GITHUB CONTAINER REGISTRY
- name: github / login to ghcr
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
username: 11notes
password: ${{ secrets.GITHUB_TOKEN }}
# REDHAT QUAY
- name: quay / login to quay
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: quay.io
username: 11notes+github
password: ${{ secrets.QUAY_TOKEN }}
- name: docker / setup qemu
if: env.WORKFLOW_BUILD == 'true'
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a
- name: docker / setup buildx
# ╔═════════════════════════════════════════════════════╗
# ║ BUILD CONTAINER IMAGE ║
# ╚═════════════════════════════════════════════════════╝
# SETUP QEMU
- name: container image / setup qemu
if: env.WORKFLOW_BUILD == 'true' && matrix.platform == 'arm/v7'
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
with:
image: tonistiigi/binfmt:qemu-v8.1.5
cache-image: false
# SETUP BUILDX BUILDER WITH USING LOCAL REGISTRY
- name: container image / setup buildx
if: env.WORKFLOW_BUILD == 'true'
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
with:
driver-opts: network=host
- name: docker / build & push & tag grype
# BUILD CONTAINER IMAGE FROM GLOBAL CACHE (DOCKER HUB) AND PUSH TO LOCAL CACHE
- name: container image / build
if: env.WORKFLOW_BUILD == 'true'
id: docker-build
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d
id: image-build
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
no-cache: ${{ env.WORKFLOW_BUILD_NO_CACHE }}
file: ${{ env.DOCKER_IMAGE_DOCKERFILE }}
push: true
platforms: ${{ env.DOCKER_IMAGE_ARCH }}
platforms: linux/${{ matrix.platform }}
cache-from: type=registry,ref=${{ env.DOCKER_CACHE_NAME }}
cache-to: type=registry,ref=${{ env.DOCKER_CACHE_REGISTRY }}${{ env.DOCKER_CACHE_NAME }},mode=max,compression=zstd,force-compression=true
build-args: |
@@ -241,10 +374,11 @@ jobs:
tags: |
${{ env.DOCKER_CACHE_GRYPE }}
- name: grype / scan
# SCAN LOCAL CONTAINER IMAGE WITH GRYPE
- name: container image / scan with grype
if: env.WORKFLOW_BUILD == 'true'
id: grype
uses: anchore/scan-action@dc6246fcaf83ae86fcc6010b9824c30d7320729e
uses: anchore/scan-action@1638637db639e0ade3258b51db49a9a137574c3e # v6.5.1
with:
image: ${{ env.DOCKER_CACHE_GRYPE }}
fail-build: ${{ env.WORKFLOW_GRYPE_FAIL_ON_SEVERITY }}
@@ -253,9 +387,10 @@ jobs:
by-cve: true
cache-db: true
- name: grype / fail
if: env.WORKFLOW_BUILD == 'true' && (failure() || steps.grype.outcome == 'failure')
uses: anchore/scan-action@dc6246fcaf83ae86fcc6010b9824c30d7320729e
# OUTPUT CVE REPORT IF SCAN FAILS
- name: container image / scan with grype FAILED
if: env.WORKFLOW_BUILD == 'true' && (failure() || steps.grype.outcome == 'failure') && steps.image-build.outcome == 'success'
uses: anchore/scan-action@1638637db639e0ade3258b51db49a9a137574c3e # v6.5.1
with:
image: ${{ env.DOCKER_CACHE_GRYPE }}
fail-build: false
@@ -264,16 +399,19 @@ jobs:
by-cve: true
cache-db: true
- name: docker / build & push
# PUSH IMAGE TO ALL REGISTRIES IF CLEAN
- name: container image / push to registries
id: image-push
if: env.WORKFLOW_BUILD == 'true'
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
no-cache: ${{ env.WORKFLOW_BUILD_NO_CACHE }}
file: ${{ env.DOCKER_IMAGE_DOCKERFILE }}
push: true
sbom: true
provenance: mode=max
platforms: ${{ env.DOCKER_IMAGE_ARCH }}
platforms: linux/${{ matrix.platform }}
cache-from: type=registry,ref=${{ env.DOCKER_CACHE_REGISTRY }}${{ env.DOCKER_CACHE_NAME }}
cache-to: type=registry,ref=${{ env.DOCKER_CACHE_NAME }},mode=max,compression=zstd,force-compression=true
build-args: |
@@ -281,38 +419,46 @@ jobs:
tags: |
${{ env.DOCKER_IMAGE_TAGS }}
# CREATE ATTESTATION ARTIFACTS
- name: container image / create attestation artifacts
if: env.WORKFLOW_BUILD == 'true' && steps.image-push.outcome == 'success'
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
with:
subject-name: docker.io/${{ env.DOCKER_IMAGE_NAME }}
subject-digest: ${{ steps.image-push.outputs.digest }}
push-to-registry: false
# RELEASE
- name: github / release / log
continue-on-error: true
id: git-log
# EXPORT DIGEST
- name: container image / export digest
if: env.WORKFLOW_BUILD == 'true' && steps.image-push.outcome == 'success'
run: |
LOCAL_LAST_TAG=$(git describe --abbrev=0 --tags `git rev-list --tags --skip=1 --max-count=1`)
echo "using last tag: ${LOCAL_LAST_TAG}"
LOCAL_COMMITS=$(git log ${LOCAL_LAST_TAG}..HEAD --oneline)
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.image-push.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "commits<<${EOF}" >> ${GITHUB_OUTPUT}
echo "${LOCAL_COMMITS}" >> ${GITHUB_OUTPUT}
echo "${EOF}" >> ${GITHUB_OUTPUT}
# UPLOAD DIGEST
- name: container image / upload
if: env.WORKFLOW_BUILD == 'true' && steps.image-push.outcome == 'success'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: digests-linux-${{ env.DOCKER_IMAGE_PLATFORM_SANITIZED }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
- name: github / release / markdown
if: env.WORKFLOW_CREATE_RELEASE == 'true' && steps.git-log.outcome == 'success'
# ╔═════════════════════════════════════════════════════╗
# ║ CREATE GITHUB RELEASE ║
# ╚═════════════════════════════════════════════════════╝
# CREATE RELEASE MARKUP
- name: github release / prepare markdown
if: env.WORKFLOW_CREATE_RELEASE == 'true' && matrix.platform == 'amd64'
id: git-release
uses: 11notes/action-docker-release@v1
# WHY IS THIS ACTION NOT SHA256 PINNED? SECURITY MUCH?!?!?!
# ---------------------------------------------------------------------------------
# the next step "github / release / create" creates a new release based on the code
# in the repo. This code is not modified and can't be modified by this action.
# It does create the markdown for the release, which could be abused, but to what
# extend? Adding a link to a malicious repo?
with:
git_log: ${{ steps.git-log.outputs.commits }}
- name: github / release / create
# CREATE GITHUB RELEASE
- name: github release / create
if: env.WORKFLOW_CREATE_RELEASE == 'true' && steps.git-release.outcome == 'success'
uses: actions/create-release@4c11c9fe1dcd9636620a16455165783b20fc7ea0
uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # v1.1.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -323,74 +469,165 @@ jobs:
prerelease: false
# ╔═════════════════════════════════════════════════════╗
# ║ ║
# ║ ║
# ║ MERGE IMAGES INTO SINGLE MANIFEST ║
# ║ ║
# ║ ║
# ╚═════════════════════════════════════════════════════╝
merge_platform_images:
needs: docker
if: needs.docker.outputs.WORKFLOW_BUILD == 'true'
name: merge platform images to a single manifest
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
registry: [docker.io, ghcr.io, quay.io]
env:
DOCKER_IMAGE_NAME: ${{ needs.docker.outputs.DOCKER_IMAGE_NAME }}
DOCKER_IMAGE_MERGE_TAGS: ${{ needs.docker.outputs.DOCKER_IMAGE_MERGE_TAGS }}
# LICENSE
- name: license / update year
continue-on-error: true
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
# ╔═════════════════════════════════════════════════════╗
# ║ CONTAINER REGISTRY LOGIN ║
# ╚═════════════════════════════════════════════════════╝
# DOCKER HUB
- name: docker / login to hub
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
script: |
const { existsSync, readFileSync, writeFileSync } = require('node:fs');
const { resolve } = require('node:path');
const file = 'LICENSE';
const year = new Date().getFullYear();
try{
const path = resolve(file);
if(existsSync(path)){
let license = readFileSync(file).toString();
if(!new RegExp(`Copyright \\(c\\) ${year} 11notes`, 'i').test(license)){
license = license.replace(/Copyright \(c\) \d{4} /i, `Copyright (c) ${new Date().getFullYear()} `);
writeFileSync(path, license);
}
}else{
throw new Error(`file ${file} does not exist`);
}
}catch(e){
core.setFailed(e);
}
username: 11notes
password: ${{ secrets.DOCKER_TOKEN }}
# GITHUB CONTAINER REGISTRY
- name: github / login to ghcr
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
username: 11notes
password: ${{ secrets.GITHUB_TOKEN }}
# REDHAT QUAY
- name: quay / login to quay
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: quay.io
username: 11notes+github
password: ${{ secrets.QUAY_TOKEN }}
# ╔═════════════════════════════════════════════════════╗
# ║ MERGE PLATFORM IMAGES MANIFEST ║
# ╚═════════════════════════════════════════════════════╝
# DOWNLOAD DIGESTS
- name: platform merge / digest
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
# SETUP BUILDX BUILDER
- name: platform merge / buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
# GET META DATA
- name: platform merge / meta
id: meta
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
with:
images: ${{ matrix.registry }}/${{ env.DOCKER_IMAGE_NAME }}
tags: |
${{ env.DOCKER_IMAGE_MERGE_TAGS }}
# CREATE MANIFEST
- name: platform merge / create manifest and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'docker.io/${{ env.DOCKER_IMAGE_NAME }}@sha256:%s ' *)
# INSPECT MANIFEST
- name: platform merge / inspect
run: |
docker buildx imagetools inspect ${{ matrix.registry }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
# README
- name: github / checkout HEAD
# ╔═════════════════════════════════════════════════════╗
# ║ ║
# ║ ║
# ║ FINALIZE IMAGE CREATION ║
# ║ ║
# ║ ║
# ╚═════════════════════════════════════════════════════╝
finally:
if: ${{ always() }}
needs:
- docker
- merge_platform_images
name: finalize image creation
runs-on: ubuntu-latest
env:
DOCKER_IMAGE_NAME: ${{ needs.docker.outputs.DOCKER_IMAGE_NAME }}
DOCKER_IMAGE_DESCRIPTION: ${{ needs.docker.outputs.DOCKER_IMAGE_DESCRIPTION }}
DOCKER_IMAGE_NAME_AND_VERSION: ${{ needs.docker.outputs.DOCKER_IMAGE_NAME_AND_VERSION }}
DOCKER_IMAGE_ARGUMENTS: ${{ needs.docker.outputs.DOCKER_IMAGE_ARGUMENTS }}
permissions:
contents: write
steps:
# ╔═════════════════════════════════════════════════════╗
# ║ SETUP ENVIRONMENT ║
# ╚═════════════════════════════════════════════════════╝
# CHECKOUT ALL DEPTHS (ALL TAGS)
- name: init / checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: master
fetch-depth: 0
# ╔═════════════════════════════════════════════════════╗
# ║ CONTAINER REGISTRY LOGIN ║
# ╚═════════════════════════════════════════════════════╝
# DOCKER HUB
- name: docker / login to hub
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
username: 11notes
password: ${{ secrets.DOCKER_TOKEN }}
# ╔═════════════════════════════════════════════════════╗
# ║ CREATE README.md ║
# ╚═════════════════════════════════════════════════════╝
# CHECKOUT HEAD TO BE UP TO DATE WITH EVERYTHING
- name: README.md / checkout
if: github.event.inputs.readme == 'true'
continue-on-error: true
run: |
run: |
git checkout HEAD
- name: docker / setup comparison images
if: env.WORKFLOW_CREATE_COMPARISON == 'true'
continue-on-error: true
run: |
docker image pull ${{ env.WORKFLOW_CREATE_COMPARISON_IMAGE }}
docker image ls --filter "reference=${{ env.WORKFLOW_CREATE_COMPARISON_IMAGE }}" --format json | jq --raw-output '.Size' &> ./comparison.size0.log
docker image pull ${{ env.WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE }}
docker image ls --filter "reference=${{ env.WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE }}" --format json | jq --raw-output '.Size' &> ./comparison.size1.log
docker run --entrypoint "/bin/sh" --rm ${{ env.WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE }} -c id &> ./comparison.id.log
- name: github / create README.md
# CREATE MAKRDOWN OF README.md
- name: README.md / create
if: github.event.inputs.readme == 'true'
id: github-readme
continue-on-error: true
if: env.WORKFLOW_CREATE_README == 'true'
uses: 11notes/action-docker-readme@v1
# WHY IS THIS ACTION NOT SHA256 PINNED? SECURITY MUCH?!?!?!
# ---------------------------------------------------------------------------------
# the next step "github / commit & push" only adds the README and LICENSE as well as
# compose.yaml to the repository. This does not pose a security risk if this action
# would be compromised. The code of the app can't be changed by this action. Since
# only the files mentioned are commited to the repo. Sure, someone could make a bad
# compose.yaml, but since this serves only as an example I see no harm in that.
with:
sarif_file: ${{ steps.grype.outputs.sarif }}
build_output_metadata: ${{ steps.docker-build.outputs.metadata }}
- name: docker / push README.md to docker hub
# UPLOAD README.md to DOCKER HUB
- name: README.md / push to Docker Hub
if: github.event.inputs.readme == 'true' && steps.github-readme.outcome == 'success' && hashFiles('README_NONGITHUB.md') != ''
continue-on-error: true
if: steps.github-readme.outcome == 'success' && hashFiles('README_NONGITHUB.md') != ''
uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8
uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8 # v1
env:
DOCKER_USER: 11notes
DOCKER_PASS: ${{ secrets.DOCKER_TOKEN }}
@@ -400,26 +637,30 @@ jobs:
short_description: ${{ env.DOCKER_IMAGE_DESCRIPTION }}
readme_file: 'README_NONGITHUB.md'
- name: github / commit & push
# COMMIT NEW README.md, LICENSE and compose
- name: README.md / github commit & push
if: github.event.inputs.readme == 'true' && steps.github-readme.outcome == 'success' && hashFiles('README.md') != ''
continue-on-error: true
if: steps.github-readme.outcome == 'success' && hashFiles('README.md') != ''
run: |
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add README.md
if [ -f compose.yaml ]; then
git add compose.yaml
fi
if [ -f compose.yml ]; then
git add compose.yml
fi
if [ -f LICENSE ]; then
git add LICENSE
fi
git commit -m "github-actions[bot]: update README.md"
git commit -m "update README.md"
git push origin HEAD:master
# REPOSITORY SETTINGS
# ╔═════════════════════════════════════════════════════╗
# ║ GITHUB REPOSITORY DEFAULT SETTINGS ║
# ╚═════════════════════════════════════════════════════╝
# UPDATE REPO WITH DEFAULT SETTINGS FOR CONTAINER IMAGE
- name: github / update description and set repo defaults
run: |
curl --request PATCH \

33
.github/workflows/version.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: version
on:
workflow_dispatch:
inputs:
version:
description: 'set version for build'
type: string
required: true
jobs:
version:
runs-on: ubuntu-latest
steps:
# ╔═════════════════════════════════════════════════════╗
# ║ BUILD VERSION {N} IMAGE ║
# ╚═════════════════════════════════════════════════════╝
- name: version / setup config
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
with:
script: |
const { Buffer } = require('node:buffer');
const etc = {
version:"${{ github.event.inputs.version }}",
semver:{disable:{rolling: true}}
};
core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64'));
- name: version / build container image
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7
with:
wait-for-completion: false
workflow: docker.yml
token: "${{ secrets.REPOSITORY_TOKEN }}"
inputs: '{ "release":"false", "readme":"false", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }'

26
.json
View File

@@ -1,20 +1,18 @@
{
"image":"11notes/kms",
"name":"kms",
"root":"/kms",
"arch":"linux/amd64,linux/arm64,linux/arm/v7",
"semver":{
"version":"1.0.1"
"image": "11notes/kms",
"name": "kms",
"root": "/kms",
"arch": "linux/amd64,linux/arm64,linux/arm/v7",
"semver": {
"version": "1.0.3"
},
"readme":{
"description":"Activate any version of Windows and Office, forever",
"parent":{
"image":"11notes/alpine:stable"
"readme": {
"description": "Activate any version of Windows and Office, forever",
"parent": {
"image": "11notes/python:3.13"
},
"built":{
"11notes/py-kms":"https://github.com/11notes/fork-py-kms"
"built": {
"11notes/py-kms": "https://github.com/11notes/fork-py-kms"
}
}
}

View File

@@ -1,7 +1,7 @@
![banner](https://github.com/11notes/defaults/blob/main/static/img/banner.png?raw=true)
# KMS
[<img src="https://img.shields.io/badge/github-source-blue?logo=github&color=040308">](https://github.com/11notes/docker-KMS)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![size](https://img.shields.io/docker/image-size/11notes/kms/1.0.1?color=0eb305)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![version](https://img.shields.io/docker/v/11notes/kms/1.0.1?color=eb7a09)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![pulls](https://img.shields.io/docker/pulls/11notes/kms?color=2b75d6)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)[<img src="https://img.shields.io/github/issues/11notes/docker-KMS?color=7842f5">](https://github.com/11notes/docker-KMS/issues)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![swiss_made](https://img.shields.io/badge/Swiss_Made-FFFFFF?labelColor=FF0000&logo=data:image/svg%2bxml;base64,PHN2ZyB2ZXJzaW9uPSIxIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0wIDBoMzJ2MzJoLTMyeiIgZmlsbD0iI2YwMCIvPjxwYXRoIGQ9Im0xMyA2aDZ2N2g3djZoLTd2N2gtNnYtN2gtN3YtNmg3eiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==)
![size](https://img.shields.io/docker/image-size/11notes/kms/1.0.3?color=0eb305)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![version](https://img.shields.io/docker/v/11notes/kms/1.0.3?color=eb7a09)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![pulls](https://img.shields.io/docker/pulls/11notes/kms?color=2b75d6)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)[<img src="https://img.shields.io/github/issues/11notes/docker-KMS?color=7842f5">](https://github.com/11notes/docker-KMS/issues)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![swiss_made](https://img.shields.io/badge/Swiss_Made-FFFFFF?labelColor=FF0000&logo=data:image/svg%2bxml;base64,PHN2ZyB2ZXJzaW9uPSIxIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgZmlsbD0idHJhbnNwYXJlbnQiLz4KICA8cGF0aCBkPSJtMTMgNmg2djdoN3Y2aC03djdoLTZ2LTdoLTd2LTZoN3oiIGZpbGw9IiNmZmYiLz4KPC9zdmc+)
Activate any version of Windows and Office, forever
@@ -42,17 +42,19 @@ Works with:
name: "kms"
services:
app:
image: "11notes/kms:1.0.1"
image: "11notes/kms:1.0.3"
environment:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "1688:1688/tcp"
restart: "always"
gui:
image: "11notes/kms-gui:1.0.1"
image: "11notes/kms-gui:1.0.3"
depends_on:
app:
condition: "service_healthy"
@@ -61,28 +63,47 @@ services:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "3000:3000/tcp"
restart: "always"
volumes:
var:
networks:
frontend:
```
To find out how you can change the default UID/GID of this container image, consult the [how-to.changeUIDGID](https://github.com/11notes/RTFM/blob/main/linux/container/image/11notes/how-to.changeUIDGID.md#change-uidgid-the-correct-way) section of my [RTFM](https://github.com/11notes/RTFM)
# EXAMPLE
## Windows Server 2025 Datacenter. List of [GVLK](https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys)
## Add your product key
Windows Server 2025 Datacenter. List of [GVLK](https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys)
```cmd
slmgr /ipk D764K-2NDRG-47T6Q-P8T8W-YP6DF
```
Add your KMS server information to server via registry
## Add your KMS server information
... via CLI
```
slmgr /skms KMS_IP:KMS_PORT
```
... via registry (or add these key to your GPO)
```powershell
"Windows"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform" -Name "KeyManagementServiceName" -Value "KMS_IP"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform" -Name "KeyManagementServicePort" -Value "KMS_PORT"
"Office"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform" -Name "KeyManagementServiceName" -Value "KMS_IP"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform" -Name "KeyManagementServicePort" -Value "KMS_PORT"
```
Activate server
... via DNS
```sh
# BIND
_vlmcs._tcp SRV 0 0 KMS_PORT KMS_IP
```
## Activate server
```cmd
slmgr /ato
```
@@ -104,24 +125,23 @@ slmgr /ato
| `KMS_LOCALE` | see Microsoft LICD specification | 1033 (en-US) |
| `KMS_ACTIVATIONINTERVAL` | Retry unsuccessful after N minutes | 120 (2 hours) |
| `KMS_RENEWALINTERVAL` | re-activation after N minutes | 259200 (180 days) |
| `KMS_LOGLEVEL` | CRITICAL, ERROR, WARNING, INFO, DEBUG, MININFO | INFO |
# MAIN TAGS 🏷️
These are the main tags for the image. There is also a tag for each commit and its shorthand sha256 value.
* [1.0.1](https://hub.docker.com/r/11notes/kms/tags?name=1.0.1)
* [1.0.1-unraid](https://hub.docker.com/r/11notes/kms/tags?name=1.0.1-unraid)
* [1.0.3](https://hub.docker.com/r/11notes/kms/tags?name=1.0.3)
* [1.0.3-unraid](https://hub.docker.com/r/11notes/kms/tags?name=1.0.3-unraid)
### There is no latest tag, what am I supposed to do about updates?
It is of my opinion that the ```:latest``` tag is super dangerous. Many times, Ive introduced **breaking** changes to my images. This would have messed up everything for some people. If you dont want to change the tag to the latest [semver](https://semver.org/), simply use the short versions of [semver](https://semver.org/). Instead of using ```:1.0.1``` you can use ```:1``` or ```:1.0```. Since on each new version these tags are updated to the latest version of the software, using them is identical to using ```:latest``` but at least fixed to a major or minor version.
It is of my opinion that the ```:latest``` tag is dangerous. Many times, Ive introduced **breaking** changes to my images. This would have messed up everything for some people. If you dont want to change the tag to the latest [semver](https://semver.org/), simply use the short versions of [semver](https://semver.org/). Instead of using ```:1.0.3``` you can use ```:1``` or ```:1.0```. Since on each new version these tags are updated to the latest version of the software, using them is identical to using ```:latest``` but at least fixed to a major or minor version.
If you still insist on having the bleeding edge release of this app, simply use the ```:rolling``` tag, but be warned! You will get the latest version of the app instantly, regardless of breaking changes or security issues or what so ever. You do this at your own risk!
# REGISTRIES ☁️
```
docker pull 11notes/kms:1.0.1
docker pull ghcr.io/11notes/kms:1.0.1
docker pull quay.io/11notes/kms:1.0.1
docker pull 11notes/kms:1.0.3
docker pull ghcr.io/11notes/kms:1.0.3
docker pull quay.io/11notes/kms:1.0.3
```
# UNRAID VERSION 🟠
@@ -131,7 +151,7 @@ This image supports unraid by default. Simply add **-unraid** to any tag and the
* [11notes/kms](https://github.com/11notes/docker-KMS)
# PARENT IMAGE 🏛️
* [11notes/alpine:stable](https://hub.docker.com/r/11notes/alpine)
* [11notes/python:3.13](${{ json_readme_parent_url }})
# BUILT WITH 🧰
* [11notes/py-kms](https://github.com/11notes/fork-py-kms)
@@ -148,4 +168,4 @@ This image supports unraid by default. Simply add **-unraid** to any tag and the
# ElevenNotes™
This image is provided to you at your own risk. Always make backups before updating an image to a different version. Check the [releases](https://github.com/11notes/docker-kms/releases) for breaking changes. If you have any problems with using this image simply raise an [issue](https://github.com/11notes/docker-kms/issues), thanks. If you have a question or inputs please create a new [discussion](https://github.com/11notes/docker-kms/discussions) instead of an issue. You can find all my other repositories on [github](https://github.com/11notes?tab=repositories).
*created 21.05.2025, 08:27:53 (CET)*
*created 23.10.2025, 07:06:45 (CET)*

View File

@@ -1,77 +1,95 @@
ARG APP_UID=1000
ARG APP_GID=1000
ARG BUILD_ROOT=/git/fork-py-kms
# ╔═════════════════════════════════════════════════════╗
# ║ SETUP ║
# ╚═════════════════════════════════════════════════════╝
# GLOBAL
ARG APP_UID=1000 \
APP_GID=1000 \
BUILD_SRC=https://github.com/11notes/fork-py-kms.git \
BUILD_ROOT=/git/fork-py-kms
# :: Util
# :: FOREIGN IMAGES
FROM 11notes/util AS util
# :: Build / py-kms
# ╔═════════════════════════════════════════════════════╗
# ║ BUILD ║
# ╚═════════════════════════════════════════════════════╝
# :: PY-KMS
FROM alpine/git AS build
ARG APP_VERSION
ARG BUILD_ROOT
ARG APP_VERSION \
BUILD_SRC \
BUILD_ROOT
RUN set -ex; \
git clone ${BUILD_SRC} -b next; \
cd ${BUILD_ROOT}; \
git checkout v${APP_VERSION};
RUN set -ex; \
git clone https://github.com/11notes/fork-py-kms -b next; \
cd ${BUILD_ROOT}; \
git checkout v${APP_VERSION}; \
cp -R ${BUILD_ROOT}/docker/docker-py3-kms-minimal/requirements.txt ${BUILD_ROOT}/py-kms/requirements.txt; \
cp -R ${BUILD_ROOT}/docker/docker-py3-kms/requirements.txt ${BUILD_ROOT}/py-kms/requirements.gui.txt;
# :: Header
FROM 11notes/alpine:stable
# ╔═════════════════════════════════════════════════════╗
# ║ IMAGE ║
# ╚═════════════════════════════════════════════════════╝
# :: HEADER
FROM 11notes/python:3.13
# :: arguments
ARG TARGETARCH
ARG APP_IMAGE
ARG APP_NAME
ARG APP_VERSION
ARG APP_ROOT
ARG APP_UID
ARG APP_GID
ARG APP_NO_CACHE
# :: default arguments
ARG TARGETPLATFORM \
TARGETOS \
TARGETARCH \
TARGETVARIANT \
APP_IMAGE \
APP_NAME \
APP_VERSION \
APP_ROOT \
APP_UID \
APP_GID \
APP_NO_CACHE
# :: default python image
ARG PIP_ROOT_USER_ACTION=ignore \
PIP_BREAK_SYSTEM_PACKAGES=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_NO_CACHE_DIR=1
# :: image specific arguments
ARG BUILD_ROOT
# :: python image
ARG PIP_ROOT_USER_ACTION=ignore
ARG PIP_BREAK_SYSTEM_PACKAGES=1
ARG PIP_DISABLE_PIP_VERSION_CHECK=1
ARG PIP_NO_CACHE_DIR=1
# :: default environment
ENV APP_IMAGE=${APP_IMAGE} \
APP_NAME=${APP_NAME} \
APP_VERSION=${APP_VERSION} \
APP_ROOT=${APP_ROOT}
# :: environment
ENV APP_IMAGE=${APP_IMAGE}
ENV APP_NAME=${APP_NAME}
ENV APP_VERSION=${APP_VERSION}
ENV APP_ROOT=${APP_ROOT}
ENV KMS_LOCALE=1033
ENV KMS_CLIENTCOUNT=26
ENV KMS_ACTIVATIONINTERVAL=120
ENV KMS_RENEWALINTERVAL=259200
ENV KMS_LOGLEVEL="INFO"
# :: app specific variables
ENV KMS_LOCALE=1033 \
KMS_ACTIVATIONINTERVAL=120 \
KMS_RENEWALINTERVAL=259200
# :: multi-stage
COPY --from=util /usr/local/bin /usr/local/bin
COPY --from=build ${BUILD_ROOT}/py-kms /opt/py-kms
# :: Run
# :: RUN
USER root
RUN eleven printenv;
# :: install application
# :: install dependencies
RUN set -ex; \
apk --no-cache --update add \
python3; \
apk --no-cache --update --virtual .build add \
py3-pip;
# :: install and update application
RUN set -ex; \
mkdir -p ${APP_ROOT}/var; \
pip3 install -r /opt/py-kms/requirements.txt; \
pip3 install pytz; \
pip3 list -o | sed 's/pip.*//' | grep . | cut -f1 -d' ' | tr " " "\n" | awk '{if(NR>=3)print}' | cut -d' ' -f1 | xargs -n1 pip3 install -U; \
apk del --no-network .build; \
rm -rf /usr/lib/python3.12/site-packages/pip;
rm -rf /usr/lib/python3.13/site-packages/pip;
# :: copy filesystem changes and set correct permissions
# :: copy root filesystem and set correct permissions
COPY ./rootfs /
RUN set -ex; \
chmod +x -R /usr/local/bin; \
@@ -79,15 +97,17 @@ ARG BUILD_ROOT=/git/fork-py-kms
${APP_ROOT} \
/opt/py-kms;
# :: support unraid
# :: enable unraid support
RUN set -ex; \
eleven unraid
# :: Volumes
# :: PERSISTENT DATA
VOLUME ["${APP_ROOT}/var"]
# :: Monitor
HEALTHCHECK --interval=5s --timeout=2s CMD netstat -an | grep -q 1688 || exit 1
# :: HEALTH
HEALTHCHECK --interval=5s --timeout=2s --start-interval=5s \
CMD ["/usr/bin/nc", "-z", "localhost", "1688"]
# :: Start
USER ${APP_UID}:${APP_GID}
# :: EXECUTE
USER ${APP_UID}:${APP_GID}
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]

View File

@@ -1,17 +1,19 @@
name: "kms"
services:
app:
image: "11notes/kms:1.0.1"
image: "11notes/kms:1.0.3"
environment:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "1688:1688/tcp"
restart: "always"
gui:
image: "11notes/kms-gui:1.0.1"
image: "11notes/kms-gui:1.0.3"
depends_on:
app:
condition: "service_healthy"
@@ -20,9 +22,14 @@ services:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "3000:3000/tcp"
restart: "always"
volumes:
var:
var:
networks:
frontend:

View File

@@ -32,19 +32,32 @@ ${{ title_volumes }}
${{ content_compose }}
# EXAMPLE
## Windows Server 2025 Datacenter. List of [GVLK](https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys)
## Add your product key
Windows Server 2025 Datacenter. List of [GVLK](https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys)
```cmd
slmgr /ipk D764K-2NDRG-47T6Q-P8T8W-YP6DF
```
Add your KMS server information to server via registry
## Add your KMS server information
... via CLI
```
slmgr /skms KMS_IP:KMS_PORT
```
... via registry (or add these key to your GPO)
```powershell
"Windows"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform" -Name "KeyManagementServiceName" -Value "KMS_IP"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform" -Name "KeyManagementServicePort" -Value "KMS_PORT"
"Office"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform" -Name "KeyManagementServiceName" -Value "KMS_IP"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform" -Name "KeyManagementServicePort" -Value "KMS_PORT"
```
Activate server
... via DNS
```sh
# BIND
_vlmcs._tcp SRV 0 0 KMS_PORT KMS_IP
```
## Activate server
```cmd
slmgr /ato
```
@@ -56,7 +69,6 @@ ${{ content_environment }}
| `KMS_LOCALE` | see Microsoft LICD specification | 1033 (en-US) |
| `KMS_ACTIVATIONINTERVAL` | Retry unsuccessful after N minutes | 120 (2 hours) |
| `KMS_RENEWALINTERVAL` | re-activation after N minutes | 259200 (180 days) |
| `KMS_LOGLEVEL` | CRITICAL, ERROR, WARNING, INFO, DEBUG, MININFO | INFO |
${{ content_source }}

View File

@@ -4,12 +4,14 @@
if [ ! -z "${DEBUG}" ]; then
KMS_LOGLEVEL="DEBUG"
eleven log debug "setting kms log level to DEBUG"
else
KMS_LOGLEVEL="INFO"
fi
cd /opt/py-kms
set -- "python3" \
pykms_Server.py \
0.0.0.0 \
:: \
1688 \
-l ${KMS_LOCALE} \
-a ${KMS_ACTIVATIONINTERVAL} \