107 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
ElevenNotes
f4f1ab656f Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-21 08:44:57 +02:00
ElevenNotes
687d4eebdc [fix] missing input version on downstream workflow for GUI 2025-05-21 08:44:45 +02:00
github-actions[bot]
a90ee477d1 github-actions[bot]: update README.md 2025-05-21 06:27:54 +00:00
ElevenNotes
274c6587ea [upgrade] to latest workflow 2025-05-21 08:20:17 +02:00
ElevenNotes
be06157c03 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-21 07:25:49 +02:00
ElevenNotes
468118bf97 [upgrade] to latest workflow 2025-05-21 07:25:40 +02:00
github-actions[bot]
485a5524eb github-actions[bot]: update README.md 2025-05-20 13:48:05 +00:00
ElevenNotes
24b5369071 [cut] KMS_CLIENTCOUNT 2025-05-20 15:42:14 +02:00
ElevenNotes
9da9b799b3 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-20 15:32:25 +02:00
ElevenNotes
b676412fc9 [cut] KMS_CLIENTCOUNT 2025-05-20 15:32:18 +02:00
ElevenNotes
89605118da [upgrade] 1.0.1 2025-05-20 15:32:05 +02:00
ElevenNotes
ed61e0a389 [fix] race condition 2025-05-20 15:30:35 +02:00
github-actions[bot]
7dfaf728ea github-actions[bot]: update README.md 2025-05-20 13:15:50 +00:00
ElevenNotes
e41bf5a487 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-20 15:07:25 +02:00
ElevenNotes
638cbd9150 [upgrade] 1.0.1 2025-05-20 15:07:15 +02:00
github-actions[bot]
55853de064 github-actions[bot]: update README.md 2025-05-19 13:39:10 +00:00
ElevenNotes
fce33aa489 [upgrade] to latest workflow 2025-05-19 09:02:11 +02:00
ElevenNotes
b9dd62fa54 [feature] add ARM v7 2025-05-19 09:01:59 +02:00
ElevenNotes
7acd95278f Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-05 19:48:53 +02:00
ElevenNotes
f254a289c2 [upgrade] to latest workflows 2025-05-05 19:48:44 +02:00
github-actions[bot]
727bf1f243 github-actions[bot]: update README.md 2025-05-05 09:03:42 +00:00
ElevenNotes
2dcd91990a [upgrade] switch to fork with semver 2025-05-05 10:58:28 +02:00
ElevenNotes
7519a01ba5 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-05 10:41:09 +02:00
ElevenNotes
b4f0d240df [upgrade] switch to https://github.com/11notes/fork-py-kms with semver 2025-05-05 10:41:01 +02:00
github-actions[bot]
cdb5a78fb4 github-actions[bot]: update README.md 2025-05-02 08:31:31 +00:00
ElevenNotes
4c77d9218e Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-02 10:27:28 +02:00
ElevenNotes
c0bf59835e [fix] invalidate cache 2025-05-02 10:27:18 +02:00
github-actions[bot]
543a33b1bf github-actions[bot]: update README.md 2025-05-02 08:18:33 +00:00
ElevenNotes
cc8b9eb8ec Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-02 10:14:22 +02:00
ElevenNotes
49b56ac50b [fix] upgrade all BUT pip 2025-05-02 10:13:56 +02:00
github-actions[bot]
84c8141758 github-actions[bot]: update README.md 2025-05-02 08:02:46 +00:00
ElevenNotes
e8aa27002d [fix] UID/GID defaults 2025-05-02 09:57:31 +02:00
ElevenNotes
c42936bf8c [fix] --break-system-packages 2025-05-02 09:52:22 +02:00
ElevenNotes
bad0decb4a Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-02 09:49:47 +02:00
ElevenNotes
914dacaaf5 [fix] no-cache-dir 2025-05-02 09:49:38 +02:00
github-actions[bot]
6ae34d7b40 github-actions[bot]: update README.md 2025-05-02 07:45:50 +00:00
ElevenNotes
98dd10e9db Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-02 09:41:26 +02:00
ElevenNotes
46a338a6eb [fix] upgrade 2025-05-02 09:41:17 +02:00
github-actions[bot]
bb7d6b68ce github-actions[bot]: update README.md 2025-05-02 07:27:31 +00:00
ElevenNotes
06b86cbc27 [upgrade] to latest workflows 2025-05-02 09:17:38 +02:00
ElevenNotes
f9031c3b01 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-05-02 08:55:46 +02:00
ElevenNotes
5453f6d93a updated workflow 2025-03-10 07:08:36 +01:00
github-actions[bot]
607ebb9cf7 auto update README.md 2025-03-07 11:11:30 +00:00
ElevenNotes
62b10178d3 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-03-07 12:08:57 +01:00
ElevenNotes
74f3f1a6d8 [fix] semver.length 2025-03-07 12:08:43 +01:00
github-actions[bot]
9da23cfa1f auto update README.md 2025-03-07 11:03:55 +00:00
ElevenNotes
88106c5ab3 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-03-07 12:01:02 +01:00
ElevenNotes
3c49769856 [upgrade] docker.yml workflow to new javascript version 2025-03-07 12:00:52 +01:00
github-actions[bot]
0731c67061 auto update README.md 2025-02-21 05:56:22 +00:00
ElevenNotes
5ad13ddfeb [feature] sql_get_all default sort by lastRequestTime DESC 2025-02-21 06:51:21 +01:00
ElevenNotes
3045fea5a5 [cut] no more static RELEASE.md 2025-02-20 06:53:12 +01:00
ElevenNotes
98df1f7f0a [feature] new release workflow (no more static RELEASE.md) 2025-02-20 06:52:42 +01:00
ElevenNotes
803d20d5e0 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-02-19 11:25:18 +01:00
ElevenNotes
cb4531c479 add run-name 2025-02-19 11:25:08 +01:00
github-actions[bot]
e340cb2fd5 update README.md 2025-02-19 10:09:40 +00:00
github-actions[bot]
6be75ef815 update README.md 2025-02-19 09:53:13 +00:00
ElevenNotes
26c465e656 Merge branch 'master' of https://github.com/11notes/docker-kms 2025-02-19 10:50:13 +01:00
ElevenNotes
c36ab2d369 add client IP to SQlite database 2025-02-19 10:50:04 +01:00
github-actions[bot]
ea186dd607 update README.md 2025-02-19 08:12:23 +00:00
ElevenNotes
5d47cf0b9f Merge branch 'master' of https://github.com/11notes/docker-kms 2025-02-19 09:02:05 +01:00
ElevenNotes
bad5f50548 11notes/action-docker-readme@v1.1.2 2025-02-19 09:01:58 +01:00
ElevenNotes
e6bf310706 remove screenshot 2025-02-19 08:31:20 +01:00
github-actions[bot]
b9c5b148a1 update README.md 2025-02-19 00:12:17 +00:00
ElevenNotes
46dab8b24f new workflow 2025-02-19 00:43:30 +01:00
ElevenNotes
b154c116cc fix markdown issue 2025-02-14 11:30:36 +01:00
ElevenNotes
66090fdadb fix healthcheck 2025-02-14 11:22:33 +01:00
ElevenNotes
58910eb75d update readme 2025-02-12 22:46:00 +01:00
ElevenNotes
06e8f2a63e typos everywhere ... 2025-02-12 22:13:27 +01:00
ElevenNotes
6ec2821901 try parallel build for normal and unraid image including GUI 2025-02-12 22:00:47 +01:00
ElevenNotes
a3a755b54e switch to the-actions-org/workflow-dispatch to chain builds 2025-02-12 21:35:53 +01:00
ElevenNotes
dd0025df2d wrong suffix 2025-02-12 11:57:46 +01:00
ElevenNotes
23231c4cbb needs: docker 2025-02-12 11:52:02 +01:00
ElevenNotes
28586cccec add unraid version 2025-02-12 11:44:28 +01:00
ElevenNotes
ce51cbe448 missing image link 2025-02-12 08:35:33 +01:00
ElevenNotes
c5b9d8f1fa Removed KMS_IP and KMS_PORT 2025-02-12 07:13:12 +01:00
26 changed files with 1243 additions and 1306 deletions

View File

@@ -1,6 +1,7 @@
# default
.git*
*.md
LICENSE
img/
maintain/
project*
LICENSE
*.md
img/
node_modules/

5
.gitattributes vendored
View File

@@ -1,2 +1,3 @@
# Auto detect text files and perform LF normalization
* text=auto
# default
* text=auto
*.sh eol=lf

115
.github/workflows/cron.update.yml vendored Normal file
View File

@@ -0,0 +1,115 @@
name: cron-update
on:
workflow_dispatch:
schedule:
- cron: "0 5 * * *"
jobs:
cron-update:
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
steps:
- name: init / checkout
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
with:
ref: 'master'
fetch-depth: 0
- name: cron-update / get latest version
run: |
echo "LATEST_VERSION=$(curl -s https://api.github.com/repos/11notes/fork-py-kms/releases/latest | jq -r '.tag_name' | sed 's/v//')" >> "${GITHUB_ENV}"
echo "LATEST_TAG=$(git describe --abbrev=0 --tags `git rev-list --tags --max-count=1` | sed 's/v//')" >> "${GITHUB_ENV}"
- name: cron-update / setup node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: '20'
- run: npm i semver
- name: cron-update / compare latest with current version
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
with:
script: |
const { existsSync, readFileSync, writeFileSync } = require('node:fs');
const { resolve } = require('node:path');
const { inspect } = require('node:util');
const semver = require('semver')
const repository = {dot:{}};
try{
const path = resolve('.json');
if(existsSync(path)){
try{
repository.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 latest = semver.valid(semver.coerce('${{ env.LATEST_VERSION }}'));
const current = semver.valid(semver.coerce(repository.dot.semver.version));
const tag = semver.valid(semver.coerce('${{ env.LATEST_TAG }}'));
if(latest && latest !== current){
core.info(`new ${semver.diff(current, latest)} release found (${latest})!`)
repository.dot.semver.version = latest;
if(tag){
core.exportVariable('WORKFLOW_NEW_TAG', semver.inc(tag, semver.diff(current, latest)));
}
if(repository.dot.semver?.latest){
repository.dot.semver.latest = repository.dot.semver.version;
}
if(repository.dot?.readme?.comparison?.image){
repository.dot.readme.comparison.image = repository.dot.readme.comparison.image.replace(current, repository.dot.semver.version);
}
try{
writeFileSync(resolve('.json'), JSON.stringify(repository.dot, null, 2));
core.exportVariable('WORKFLOW_AUTO_UPDATE', true);
}catch(e){
core.setFailed(e);
}
}else{
core.info('no new release found');
}
core.info(inspect(repository.dot, {showHidden:false, depth:null, colors:true}));
- name: cron-update / checkout
id: checkout
if: env.WORKFLOW_AUTO_UPDATE == 'true'
run: |
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 "chore: auto upgrade to ${{ env.LATEST_VERSION }}"
git push origin HEAD:master
- name: cron-update / tag
if: env.WORKFLOW_AUTO_UPDATE == 'true' && steps.checkout.outcome == 'success'
run: |
SHA256=$(git rev-list --branches --max-count=1)
git tag -a v${{ env.WORKFLOW_NEW_TAG }} -m "v${{ env.WORKFLOW_NEW_TAG }}" ${SHA256}
git push --follow-tags
- name: cron-update / build docker image
if: env.WORKFLOW_AUTO_UPDATE == 'true' && steps.checkout.outcome == 'success'
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7
with:
workflow: docker.yml
wait-for-completion: false
token: "${{ secrets.REPOSITORY_TOKEN }}"
inputs: '{ "release":"true", "readme":"true" }'
ref: "v${{ env.WORKFLOW_NEW_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

@@ -1,153 +1,666 @@
name: create and publish docker image
name: docker
run-name: ${{ inputs.run-name }}
on:
workflow_dispatch:
inputs:
run-name:
description: 'set run-name for workflow (multiple calls)'
type: string
required: false
default: 'docker'
platform:
description: 'list of platforms to build for'
type: string
required: false
default: "amd64,arm64,arm/v7"
build:
description: 'set WORKFLOW_BUILD'
required: false
default: 'true'
release:
description: 'set WORKFLOW_GITHUB_RELEASE'
required: true
default: 'true'
push:
tags:
- 'v*'
required: false
default: 'false'
env:
WORKFLOW_GITHUB_RELEASE: true
readme:
description: 'set WORKFLOW_GITHUB_README'
required: false
default: 'false'
etc:
description: 'base64 encoded json string'
required: false
jobs:
build-and-push-image:
# ╔═════════════════════════════════════════════════════╗
# ║ ║
# ║ ║
# ║ 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:
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:
registry:
image: registry:2
ports:
- 5000:5000
permissions:
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
- name: init / .json to env
uses: rgarcia-phi/json-to-variables@9835d537368468c4e4de5254dc3efeadda183793
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
filename: '.json'
ref: ${{ github.ref_name }}
fetch-depth: 0
# SETUP ENVIRONMENT VARIABLES AND INPUTS
- name: init / setup environment
run: |
: # set default arch if not set
echo "IMAGE_ARCH=${json_arch:-linux/amd64,linux/arm64}" >> $GITHUB_ENV
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) }}`.
replace(/"platform":\s*"\[(.+)\]",/i, `"platform": [$1],`);
const opt = {input:{}, dot:{}};
: # create tags for semver, stable and other shenanigans
export LOCAL_SHA=$(git rev-parse --short HEAD)
export LOCAL_SEMVER_MAJOR=$(awk -F. '{ print $1 }' <<< ${json_version})
export LOCAL_SEMVER_MINOR=$(awk -F. '{ print $2 }' <<< ${json_version})
export LOCAL_SEMVER_PATCH=$(awk -F. '{ print $3 }' <<< ${json_version})
export LOCAL_TAGS="${json_image}:${LOCAL_SHA}"
if [ ! -z ${LOCAL_SEMVER_MAJOR} ]; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_SEMVER_MAJOR}"; fi
if [ ! -z ${LOCAL_SEMVER_MINOR} ]; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_SEMVER_MAJOR}.${LOCAL_SEMVER_MINOR}"; fi
if [ ! -z ${LOCAL_SEMVER_PATCH} ]; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_SEMVER_MAJOR}.${LOCAL_SEMVER_MINOR}.${LOCAL_SEMVER_PATCH}"; fi
if echo "${LOCAL_TAGS}" | grep -q "${json_stable}" ; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:stable"; fi
if echo "${LOCAL_TAGS}" | grep -q "${json_latest}" ; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:latest"; fi
if [ ! -z ${json_tags} ]; then SPECIAL_LOCAL_TAGS=$(echo ${json_tags} | sed 's/,/ /g'); for LOCAL_TAG in ${json_tags}; do LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_TAG}"; done; fi
echo "IMAGE_TAGS=${LOCAL_TAGS}" >> $GITHUB_ENV
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');
core.warning(inputs);
}
: # if for whatever reason UID/GID must be changed at build time
echo "IMAGE_UID=${json_uid:-1000}" >> $GITHUB_ENV
echo "IMAGE_GID=${json_gid:-1000}" >> $GITHUB_ENV
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);
}
: # echo inputs
echo "${{ toJSON(github.event.inputs) }}"
const docker = {
image:{
name:opt.dot.image,
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,
name:opt.dot.name,
version:(opt.input?.etc?.version || opt.dot?.semver?.version),
root:opt.dot.root,
UID:(opt.input?.etc?.uid || 1000),
GID:(opt.input?.etc?.gid || 1000),
no_cache:new Date().getTime(),
},
cache:{
registry:'localhost:5000/',
enable:(opt.input?.etc?.cache === undefined) ? true : opt.input.etc.cache,
},
tags:[],
merge_tags:[],
};
- name: github / set workflow release
if: github.event_name == 'workflow_dispatch'
run: |
echo "WORKFLOW_GITHUB_RELEASE=${{ inputs.release }}" >> $GITHUB_ENV
docker.cache.name = `${docker.image.name}:${docker.image.prefix}buildcache${docker.image.suffix}`;
docker.cache.grype = `${docker.cache.registry}${docker.image.name}:${docker.image.prefix}grype${docker.image.suffix}`;
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 && !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(opt.input.etc.tag);
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 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]}`);
if(semver.length >= 3) docker.image.tags.push(`${semver[0]}.${semver[1]}.${semver[2]}`);
}
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.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
if(opt.input?.etc?.build?.args){
for(const arg in opt.input.etc.build.args){
docker.app[arg] = opt.input.etc.build.args[arg];
}
}
if(opt.dot?.build?.args){
for(const arg in opt.dot.build.args){
docker.app[arg] = opt.dot.build.args[arg];
}
}
const arguments = [];
for(const argument in docker.app){
arguments.push(`APP_${argument.toUpperCase()}=${docker.app[argument]}`);
}
// export to environment
core.exportVariable('DOCKER_CACHE_REGISTRY', docker.cache.registry);
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.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_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 || 'critical'));
// print
core.info(inspect({opt:opt, docker:docker}, {showHidden:false, depth:null, colors:true}));
# ╔═════════════════════════════════════════════════════╗
# ║ CONTAINER REGISTRY LOGIN ║
# ╚═════════════════════════════════════════════════════╝
# DOCKER HUB
- name: docker / login to hub
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
username: 11notes
password: ${{ secrets.DOCKER_TOKEN }}
- name: docker / setup qemu
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a
# 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 }}
- name: docker / setup buildx
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5
# 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 }}
- name: grype / build & push
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d
# ╔═════════════════════════════════════════════════════╗
# ║ 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@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
with:
driver-opts: network=host
# BUILD CONTAINER IMAGE FROM GLOBAL CACHE (DOCKER HUB) AND PUSH TO LOCAL CACHE
- name: container image / build
if: env.WORKFLOW_BUILD == 'true'
id: image-build
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: arch.dockerfile
no-cache: ${{ env.WORKFLOW_BUILD_NO_CACHE }}
file: ${{ env.DOCKER_IMAGE_DOCKERFILE }}
push: true
platforms: ${{ env.IMAGE_ARCH }}
cache-from: type=registry,ref=${{ env.json_image }}:buildcache
cache-to: type=registry,ref=${{ env.json_image }}:buildcache,mode=max,compression=zstd,force-compression=true
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: |
APP_IMAGE=${{ env.json_image }}
APP_NAME=${{ env.json_name }}
APP_VERSION=${{ env.json_version }}
APP_ROOT=${{ env.json_root }}
APP_UID=${{ env.IMAGE_UID }}
APP_GID=${{ env.IMAGE_GID }}
NO_CACHE=$(date +%s)
${{ env.DOCKER_IMAGE_ARGUMENTS }}
tags: |
${{ env.json_image }}:grype
${{ env.DOCKER_CACHE_GRYPE }}
- name: grype / scan
id: scan
uses: anchore/scan-action@abae793926ec39a78ab18002bc7fc45bbbd94342
# SCAN LOCAL CONTAINER IMAGE WITH GRYPE
- name: container image / scan with grype
if: env.WORKFLOW_BUILD == 'true'
id: grype
uses: anchore/scan-action@1638637db639e0ade3258b51db49a9a137574c3e # v6.5.1
with:
image: ${{ env.json_image }}:grype
severity-cutoff: high
image: ${{ env.DOCKER_CACHE_GRYPE }}
fail-build: ${{ env.WORKFLOW_GRYPE_FAIL_ON_SEVERITY }}
severity-cutoff: ${{ env.WORKFLOW_GRYPE_SEVERITY_CUTOFF }}
output-format: 'sarif'
by-cve: true
cache-db: true
- name: grype / report / print
if: success() || failure()
run: cat ${{ steps.scan.outputs.sarif }}
- name: grype / delete tag
if: success() || failure()
run: |
curl --request DELETE \
--url https://hub.docker.com/v2/repositories/${{ env.json_image }}/tags/grype/ \
--header 'authorization: jwt ${{ secrets.DOCKER_TOKEN }}' \
--header 'content-type: application/json' \
--fail
- name: grype / report / upload
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169
# 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:
sarif_file: ${{ steps.scan.outputs.sarif }}
image: ${{ env.DOCKER_CACHE_GRYPE }}
fail-build: false
severity-cutoff: ${{ env.WORKFLOW_GRYPE_SEVERITY_CUTOFF }}
output-format: 'table'
by-cve: true
cache-db: true
- name: docker / build & push
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d
# 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@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: arch.dockerfile
no-cache: ${{ env.WORKFLOW_BUILD_NO_CACHE }}
file: ${{ env.DOCKER_IMAGE_DOCKERFILE }}
push: true
sbom: true
provenance: mode=max
platforms: ${{ env.IMAGE_ARCH }}
cache-from: type=registry,ref=${{ env.json_image }}:buildcache
cache-to: type=registry,ref=${{ env.json_image }}:buildcache,mode=max,compression=zstd,force-compression=true
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: |
APP_IMAGE=${{ env.json_image }}
APP_NAME=${{ env.json_name }}
APP_VERSION=${{ env.json_version }}
APP_ROOT=${{ env.json_root }}
APP_UID=${{ env.IMAGE_UID }}
APP_GID=${{ env.IMAGE_GID }}
NO_CACHE=$(date +%s)
${{ env.DOCKER_IMAGE_ARGUMENTS }}
tags: |
${{ env.IMAGE_TAGS }}
${{ env.DOCKER_IMAGE_TAGS }}
- name: github / create release notes
if: env.WORKFLOW_GITHUB_RELEASE == 'true' && hashFiles('RELEASE.md') != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 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
# EXPORT DIGEST
- name: container image / export digest
if: env.WORKFLOW_BUILD == 'true' && steps.image-push.outcome == 'success'
run: |
gh release create ${{ github.ref_name }} -F RELEASE.md
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.image-push.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
# 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
# ╔═════════════════════════════════════════════════════╗
# ║ 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
# CREATE GITHUB RELEASE
- name: github release / create
if: env.WORKFLOW_CREATE_RELEASE == 'true' && steps.git-release.outcome == 'success'
uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # v1.1.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body: ${{ steps.git-release.outputs.release }}
draft: false
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 }}
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:
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 }}
# ╔═════════════════════════════════════════════════════╗
# ║ ║
# ║ ║
# ║ 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: |
git checkout HEAD
# CREATE MAKRDOWN OF README.md
- name: README.md / create
if: github.event.inputs.readme == 'true'
id: github-readme
continue-on-error: true
uses: 11notes/action-docker-readme@v1
# 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
uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8 # v1
env:
DOCKER_USER: 11notes
DOCKER_PASS: ${{ secrets.DOCKER_TOKEN }}
with:
destination_container_repo: ${{ env.DOCKER_IMAGE_NAME }}
provider: dockerhub
short_description: ${{ env.DOCKER_IMAGE_DESCRIPTION }}
readme_file: 'README_NONGITHUB.md'
# 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
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 "update README.md"
git push origin HEAD:master
# ╔═════════════════════════════════════════════════════╗
# ║ GITHUB REPOSITORY DEFAULT SETTINGS ║
# ╚═════════════════════════════════════════════════════╝
# UPDATE REPO WITH DEFAULT SETTINGS FOR CONTAINER IMAGE
- name: github / update description and set repo defaults
run: |
curl --request PATCH \
@@ -155,29 +668,11 @@ jobs:
--header 'authorization: Bearer ${{ secrets.REPOSITORY_TOKEN }}' \
--header 'content-type: application/json' \
--data '{
"description":"${{ env.json_description }}",
"description":"${{ env.DOCKER_IMAGE_DESCRIPTION }}",
"homepage":"",
"has_issues":true,
"has_discussions":true,
"has_projects":false,
"has_wiki":false
}' \
--fail
- name: docker / push README.md to docker hub
uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8
env:
DOCKER_USER: 11notes
DOCKER_PASS: ${{ secrets.DOCKER_TOKEN }}
with:
destination_container_repo: ${{ env.json_image }}
provider: dockerhub
short_description: ${{ env.json_description }}
readme_file: 'README.md'
- name: github / dispatch workflow
if: env.json_dispatch_workflow != null
uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc
with:
workflow: ${{ env.json_dispatch_workflow }}
token: "${{ secrets.REPOSITORY_TOKEN }}"
--fail

View File

@@ -1,17 +0,0 @@
name: kms-gui
on:
workflow_dispatch:
jobs:
kms-gui:
runs-on: ubuntu-latest
steps:
- name: auto build and update downstream image
uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc
with:
workflow: docker.yml
repo: 11notes/docker-kms-gui
ref: master
token: "${{ secrets.REPOSITORY_TOKEN }}"
inputs: '{ "release": "false" }'

16
.github/workflows/readme.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: readme
on:
workflow_dispatch:
jobs:
readme:
runs-on: ubuntu-latest
steps:
- name: update README.md
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7
with:
wait-for-completion: false
workflow: docker.yml
token: "${{ secrets.REPOSITORY_TOKEN }}"
inputs: '{ "build":"false", "release":"false", "readme":"true" }'

102
.github/workflows/tags.yml vendored Normal file
View File

@@ -0,0 +1,102 @@
name: tags
on:
push:
tags:
- 'v*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: build docker image
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7
with:
workflow: docker.yml
token: "${{ secrets.REPOSITORY_TOKEN }}"
inputs: '{ "release":"true", "readme":"true" }'
docker-unraid:
runs-on: ubuntu-latest
steps:
- name: init / base64 nested json
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
with:
script: |
const { Buffer } = require('node:buffer');
const etc = {
semversuffix:"unraid",
uid:99,
gid:100,
};
core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64'));
- name: build docker image for unraid community
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7
with:
workflow: docker.yml
token: "${{ secrets.REPOSITORY_TOKEN }}"
inputs: '{ "release":"false", "readme":"false", "run-name":"unraid", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }'
kms-gui:
runs-on: ubuntu-latest
needs: docker
steps:
- name: init / base64 nested json
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
with:
script: |
const { Buffer } = require('node:buffer');
(async()=>{
try{
const master = await fetch('https://raw.githubusercontent.com/11notes/docker-kms/refs/heads/master/.json');
const dot = await master.json();
const etc = {
version:dot.semver.version,
};
core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64'));
}catch(e){
core.setFailed(`workflow failed: ${e}`);
}
})();
- name: build downstream kms gui
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7
with:
workflow: docker.yml
token: "${{ secrets.REPOSITORY_TOKEN }}"
repo: 11notes/docker-kms-gui
ref: master
inputs: '{ "release":"false", "readme":"true", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }'
kms-gui-unraid:
runs-on: ubuntu-latest
needs: docker-unraid
steps:
- name: init / base64 nested json
uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298
with:
script: |
const { Buffer } = require('node:buffer');
(async()=>{
try{
const master = await fetch('https://raw.githubusercontent.com/11notes/docker-kms/refs/heads/master/.json');
const dot = await master.json();
const etc = {
version:dot.semver.version,
semversuffix:"unraid",
uid:99,
gid:100,
};
core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64'));
}catch(e){
core.setFailed(`workflow failed: ${e}`);
}
})();
- name: build downstream kms gui for unraid community
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7
with:
workflow: docker.yml
token: "${{ secrets.REPOSITORY_TOKEN }}"
repo: 11notes/docker-kms-gui
ref: master
inputs: '{ "release":"false", "readme":"false", "run-name":"unraid", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }'

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 }}" }'

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
# default
maintain/
project*
node_modules/

27
.json
View File

@@ -1,15 +1,18 @@
{
"image":"11notes/kms",
"description":"Activate any version of Windows and Office, forever",
"name":"kms",
"version":"465f4d1",
"root":"/kms",
"stable":"465f4d1",
"latest":"465f4d1",
"parent":"11notes/alpine:stable",
"dispatch":{
"workflow":"gui.yml"
"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/python:3.13"
},
"built": {
"11notes/py-kms": "https://github.com/11notes/fork-py-kms"
}
}
}

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 11notes
Copyright (c) 2025 11notes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

108
README.md
View File

@@ -1,15 +1,16 @@
![Banner](https://github.com/11notes/defaults/blob/main/static/img/banner.png?raw=true)
![banner](https://github.com/11notes/defaults/blob/main/static/img/banner.png?raw=true)
# 🏔️ kms on Alpine
[<img src="https://img.shields.io/badge/github-source-blue?logo=github&color=040308">](https://github.com/11notes/docker-kms)![size](https://img.shields.io/docker/image-size/11notes/kms/465f4d1?color=0eb305)![version](https://img.shields.io/docker/v/11notes/kms/465f4d1?color=eb7a09)![pulls](https://img.shields.io/docker/pulls/11notes/kms?color=2b75d6)[<img src="https://img.shields.io/github/issues/11notes/docker-kms?color=7842f5">](https://github.com/11notes/docker-kms/issues)
# KMS
![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**
Activate any version of Windows and Office, forever
![activation](https://github.com/11notes/docker-kms/blob/master/img/activation.png "Windows Server 2025 Datacenter")
![GUI](https://github.com/11notes/docker-kms/blob/master/img/GUI.png "11notes/kms-gui")
![Windows Server 2025](https://github.com/11notes/docker-KMS/blob/master/img/WindowsSRV2025.png?raw=true)
![Web GUI](https://github.com/11notes/docker-KMS/blob/master/img/webGUICustomIcon.png?raw=true)
# SYNOPSIS 📖
**What can I do with this?** This image will run a KMS server you can use to activate any version of Windows and Office, forever. If you need a GUI, simply add [11notes/kms-gui](https://github.com/11notes/docker-kms-gui) to your compose.
**What can I do with this?** This image will run a KMS server you can use to activate any version of Windows and Office, forever.
Works with:
- Windows Vista
@@ -40,43 +41,69 @@ Works with:
```yaml
name: "kms"
services:
kms:
image: "11notes/kms:465f4d1"
container_name: "kms"
app:
image: "11notes/kms:1.0.3"
environment:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "1688:1688/tcp"
restart: "always"
kms-gui:
image: "11notes/kms-gui:latest"
container_name: "kms-gui"
gui:
image: "11notes/kms-gui:1.0.3"
depends_on:
app:
condition: "service_healthy"
restart: true
environment:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "8080:8080/tcp"
- "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)
## Windows Server 2025 Datacenter. List of [GVLK](https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys)
# EXAMPLE
## 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
## 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
```
@@ -95,29 +122,50 @@ slmgr /ato
| --- | --- | --- |
| `TZ` | [Time Zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | |
| `DEBUG` | Will activate debug option for container image and app (if available) | |
| `KMS_IP` | localhost or 127.0.0.1 or a dedicated IP | 0.0.0.0 |
| `KMS_PORT` | any port > 1024 | 1688 |
| `KMS_LOCALE` | see Microsoft LICD specification | 1033 (en-US) |
| `KMS_CLIENTCOUNT` | client count > 25 | 26 |
| `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.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 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.3
docker pull ghcr.io/11notes/kms:1.0.3
docker pull quay.io/11notes/kms:1.0.3
```
# UNRAID VERSION 🟠
This image supports unraid by default. Simply add **-unraid** to any tag and the image will run as 99:100 instead of 1000:1000 causing no issues on unraid. Enjoy.
# SOURCE 💾
* [11notes/kms](https://github.com/11notes/docker-kms)
* [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 🧰
* [py-kms](https://github.com/Py-KMS-Organization/py-kms)
* [alpine](https://alpinelinux.org)
* [11notes/py-kms](https://github.com/11notes/fork-py-kms)
* [11notes/util](https://github.com/11notes/docker-util)
# GENERAL TIPS 📌
* Use a reverse proxy like Traefik, Nginx, HAproxy to terminate TLS and to protect your endpoints
* Use Lets Encrypt DNS-01 challenge to obtain valid SSL certificates for your services
> [!TIP]
>* Use a reverse proxy like Traefik, Nginx, HAproxy to terminate TLS and to protect your endpoints
>* Use Lets Encrypt DNS-01 challenge to obtain valid SSL certificates for your services
* Do not expose this image to WAN! You will get notified from Microsoft via your ISP to terminate the service if you do so
* [Microsoft LICD](https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a)
* Use [11notes/kms-gui](https://github.com/11notes/docker-kms-gui) if you want to see the clients you activated in a nice web GUI
# 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. You can find all my repositories on [github](https://github.com/11notes?tab=repositories).
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 23.10.2025, 07:06:45 (CET)*

View File

@@ -1,3 +0,0 @@
### 🪄 Features
* add Server 2025 KMS IDs by @ProfessorCha0s (thanks)
* add Office LTSC 2024 KMS IDs by @MrRubberDucky (thanks)

View File

@@ -1,73 +1,113 @@
# :: Util
# ╔═════════════════════════════════════════════════════╗
# ║ 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
# :: FOREIGN IMAGES
FROM 11notes/util AS util
# :: Build / py-kms
# ╔═════════════════════════════════════════════════════╗
# ║ BUILD ║
# ╚═════════════════════════════════════════════════════╝
# :: PY-KMS
FROM alpine/git AS build
ARG APP_VERSION
ARG APP_VERSION \
BUILD_SRC \
BUILD_ROOT
RUN set -ex; \
git clone https://github.com/Py-KMS-Organization/py-kms.git -b next; \
cd /git/py-kms; \
git checkout ${APP_VERSION}; \
cp -R /git/py-kms/docker/docker-py3-kms-minimal/requirements.txt /git/py-kms/py-kms/requirements.txt; \
cp -R /git/py-kms/docker/docker-py3-kms/requirements.txt /git/py-kms/py-kms/requirements.gui.txt;
git clone ${BUILD_SRC} -b next; \
cd ${BUILD_ROOT}; \
git checkout v${APP_VERSION};
# :: Header
FROM 11notes/alpine:stable
RUN set -ex; \
cd ${BUILD_ROOT}; \
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;
# :: arguments
ARG TARGETARCH
ARG APP_IMAGE
ARG APP_NAME
ARG APP_VERSION
ARG APP_ROOT
# ╔═════════════════════════════════════════════════════╗
# ║ IMAGE ║
# ╚═════════════════════════════════════════════════════╝
# :: HEADER
FROM 11notes/python:3.13
# :: environment
ENV APP_IMAGE=${APP_IMAGE}
ENV APP_NAME=${APP_NAME}
ENV APP_VERSION=${APP_VERSION}
ENV APP_ROOT=${APP_ROOT}
# :: default arguments
ARG TARGETPLATFORM \
TARGETOS \
TARGETARCH \
TARGETVARIANT \
APP_IMAGE \
APP_NAME \
APP_VERSION \
APP_ROOT \
APP_UID \
APP_GID \
APP_NO_CACHE
ENV KMS_IP=0.0.0.0
ENV KMS_PORT=1688
ENV KMS_LOCALE=1033
ENV KMS_CLIENTCOUNT=26
ENV KMS_ACTIVATIONINTERVAL=120
ENV KMS_RENEWALINTERVAL=259200
ENV KMS_LOGLEVEL="INFO"
# :: 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
# :: default environment
ENV APP_IMAGE=${APP_IMAGE} \
APP_NAME=${APP_NAME} \
APP_VERSION=${APP_VERSION} \
APP_ROOT=${APP_ROOT}
# :: 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 /git/py-kms/py-kms/ /opt/py-kms
COPY --from=util /usr/local/bin /usr/local/bin
COPY --from=build ${BUILD_ROOT}/py-kms /opt/py-kms
# :: Run
# :: RUN
USER root
# :: 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 --no-cache-dir -r /opt/py-kms/requirements.txt --break-system-packages; \
pip3 install --no-cache-dir pytz --break-system-packages; \
apk del --no-network .build;
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.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; \
chown -R 1000:1000 \
chown -R ${APP_UID}:${APP_GID} \
${APP_ROOT} \
/opt/py-kms;
# :: Volumes
# :: enable unraid support
RUN set -ex; \
eleven unraid
# :: PERSISTENT DATA
VOLUME ["${APP_ROOT}/var"]
# :: Monitor
HEALTHCHECK --interval=5s --timeout=2s CMD /usr/local/bin/healthcheck.sh || exit 1
# :: HEALTH
HEALTHCHECK --interval=5s --timeout=2s --start-interval=5s \
CMD ["/usr/bin/nc", "-z", "localhost", "1688"]
# :: Start
USER docker
# :: EXECUTE
USER ${APP_UID}:${APP_GID}
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]

View File

@@ -1,24 +1,35 @@
name: "kms"
services:
kms:
image: "11notes/kms:465f4d1"
container_name: "kms"
app:
image: "11notes/kms:1.0.3"
environment:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "1688:1688/tcp"
restart: "always"
kms-gui:
image: "11notes/kms-gui:latest"
container_name: "kms-gui"
gui:
image: "11notes/kms-gui:1.0.3"
depends_on:
app:
condition: "service_healthy"
restart: true
environment:
TZ: "Europe/Zurich"
volumes:
- "var:/kms/var"
networks:
frontend:
ports:
- "8080:8080/tcp"
- "3000:3000/tcp"
restart: "always"
volumes:
var:
var:
networks:
frontend:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
img/Office.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
img/Windows11ENTLTSC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
img/WindowsSRV2025.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

BIN
img/webGUICustomIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

82
project.md Normal file
View File

@@ -0,0 +1,82 @@
![Windows Server 2025](https://github.com/11notes/docker-${{ json_name }}/blob/master/img/WindowsSRV2025.png?raw=true)
![Web GUI](https://github.com/11notes/docker-${{ json_name }}/blob/master/img/webGUICustomIcon.png?raw=true)
${{ content_synopsis }} This image will run a KMS server you can use to activate any version of Windows and Office, forever.
Works with:
- Windows Vista
- Windows 7
- Windows 8
- Windows 8.1
- Windows 10
- Windows 11
- Windows Server 2008
- Windows Server 2008 R2
- Windows Server 2012
- Windows Server 2012 R2
- Windows Server 2016
- Windows Server 2019
- Windows Server 2022
- Windows Server 2025
- Microsoft Office 2010 ( Volume License )
- Microsoft Office 2013 ( Volume License )
- Microsoft Office 2016 ( Volume License )
- Microsoft Office 2019 ( Volume License )
- Microsoft Office 2021 ( Volume License )
- Microsoft Office 2024 ( Volume License )
${{ title_volumes }}
* **${{ json_root }}/var** - Directory of the activation database
${{ content_compose }}
# EXAMPLE
## 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
... 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"
```
... via DNS
```sh
# BIND
_vlmcs._tcp SRV 0 0 KMS_PORT KMS_IP
```
## Activate server
```cmd
slmgr /ato
```
${{ content_defaults }}
| `database` | /kms/var/kms.db | SQlite database holding all client data |
${{ 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) |
${{ content_source }}
${{ content_parent }}
${{ content_built }}
${{ content_tips }}
* Do not expose this image to WAN! You will get notified from Microsoft via your ISP to terminate the service if you do so
* [Microsoft LICD](https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a)
* Use [11notes/kms-gui](https://github.com/11notes/docker-kms-gui) if you want to see the clients you activated in a nice web GUI

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,16 @@
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 \
${KMS_IP} \
${KMS_PORT} \
:: \
1688 \
-l ${KMS_LOCALE} \
-c ${KMS_CLIENTCOUNT} \
-a ${KMS_ACTIVATIONINTERVAL} \
-r ${KMS_RENEWALINTERVAL} \
-s /kms/var/kms.db \

View File

@@ -1,2 +0,0 @@
#!/bin/ash
netstat -an | grep -q ${KMS_PORT}