mirror of
https://github.com/11notes/docker-kms.git
synced 2025-10-23 16:14:05 +00:00
Compare commits
107 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
87311cac9c | ||
|
e884b251b6 | ||
|
ba63b3ae11 | ||
|
559803f9d5 | ||
|
c1b24dfcca | ||
|
ee192d6d81 | ||
|
16e90146a4 | ||
|
aced02117a | ||
|
018a0c38d1 | ||
|
13638d92bc | ||
|
302e3765b7 | ||
|
16ec64e4ed | ||
|
b02cacc8cb | ||
|
efbc374fdf | ||
|
2fe67967b0 | ||
|
7fe09c3a65 | ||
|
abf93ebf36 | ||
|
c7ceef1895 | ||
|
79e9f980dd | ||
|
75e540239a | ||
|
3b9fdb0518 | ||
|
8744c5a656 | ||
|
d5643d374d | ||
|
febdc20df2 | ||
|
a3c4b0ccbf | ||
|
f8ec600025 | ||
|
24a9b2f00e | ||
|
2e5987e07e | ||
|
6174e7f2e3 | ||
|
bde8202670 | ||
|
0e8ba02ebc | ||
|
0a8b7acd55 | ||
|
f4f1ab656f | ||
|
687d4eebdc | ||
|
a90ee477d1 | ||
|
274c6587ea | ||
|
be06157c03 | ||
|
468118bf97 | ||
|
485a5524eb | ||
|
24b5369071 | ||
|
9da9b799b3 | ||
|
b676412fc9 | ||
|
89605118da | ||
|
ed61e0a389 | ||
|
7dfaf728ea | ||
|
e41bf5a487 | ||
|
638cbd9150 | ||
|
55853de064 | ||
|
fce33aa489 | ||
|
b9dd62fa54 | ||
|
7acd95278f | ||
|
f254a289c2 | ||
|
727bf1f243 | ||
|
2dcd91990a | ||
|
7519a01ba5 | ||
|
b4f0d240df | ||
|
cdb5a78fb4 | ||
|
4c77d9218e | ||
|
c0bf59835e | ||
|
543a33b1bf | ||
|
cc8b9eb8ec | ||
|
49b56ac50b | ||
|
84c8141758 | ||
|
e8aa27002d | ||
|
c42936bf8c | ||
|
bad0decb4a | ||
|
914dacaaf5 | ||
|
6ae34d7b40 | ||
|
98dd10e9db | ||
|
46a338a6eb | ||
|
bb7d6b68ce | ||
|
06b86cbc27 | ||
|
f9031c3b01 | ||
|
5453f6d93a | ||
|
607ebb9cf7 | ||
|
62b10178d3 | ||
|
74f3f1a6d8 | ||
|
9da23cfa1f | ||
|
88106c5ab3 | ||
|
3c49769856 | ||
|
0731c67061 | ||
|
5ad13ddfeb | ||
|
3045fea5a5 | ||
|
98df1f7f0a | ||
|
803d20d5e0 | ||
|
cb4531c479 | ||
|
e340cb2fd5 | ||
|
6be75ef815 | ||
|
26c465e656 | ||
|
c36ab2d369 | ||
|
ea186dd607 | ||
|
5d47cf0b9f | ||
|
bad5f50548 | ||
|
e6bf310706 | ||
|
b9c5b148a1 | ||
|
46dab8b24f | ||
|
b154c116cc | ||
|
66090fdadb | ||
|
58910eb75d | ||
|
06e8f2a63e | ||
|
6ec2821901 | ||
|
a3a755b54e | ||
|
dd0025df2d | ||
|
23231c4cbb | ||
|
28586cccec | ||
|
ce51cbe448 | ||
|
c5b9d8f1fa |
@@ -1,6 +1,7 @@
|
||||
# default
|
||||
.git*
|
||||
*.md
|
||||
LICENSE
|
||||
img/
|
||||
maintain/
|
||||
project*
|
||||
LICENSE
|
||||
*.md
|
||||
img/
|
||||
node_modules/
|
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -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
115
.github/workflows/cron.update.yml
vendored
Normal 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
70
.github/workflows/cve.yml
vendored
Normal 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
|
733
.github/workflows/docker.yml
vendored
733
.github/workflows/docker.yml
vendored
@@ -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
|
17
.github/workflows/gui.yml
vendored
17
.github/workflows/gui.yml
vendored
@@ -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
16
.github/workflows/readme.yml
vendored
Normal 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
102
.github/workflows/tags.yml
vendored
Normal 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
33
.github/workflows/version.yml
vendored
Normal 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
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
# default
|
||||
maintain/
|
||||
project*
|
||||
node_modules/
|
27
.json
27
.json
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
2
LICENSE
2
LICENSE
@@ -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
108
README.md
@@ -1,15 +1,16 @@
|
||||

|
||||

|
||||
|
||||
# 🏔️ kms on Alpine
|
||||
[<img src="https://img.shields.io/badge/github-source-blue?logo=github&color=040308">](https://github.com/11notes/docker-kms)[<img src="https://img.shields.io/github/issues/11notes/docker-kms?color=7842f5">](https://github.com/11notes/docker-kms/issues)
|
||||
# KMS
|
||||
[<img src="https://img.shields.io/github/issues/11notes/docker-KMS?color=7842f5">](https://github.com/11notes/docker-KMS/issues)
|
||||
|
||||
**Activate any version of Windows and Office, forever**
|
||||
Activate any version of Windows and Office, forever
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
# 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, I’ve introduced **breaking** changes to my images. This would have messed up everything for some people. If you don’t 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 Let’s 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 Let’s 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)*
|
@@ -1,3 +0,0 @@
|
||||
### 🪄 Features
|
||||
* add Server 2025 KMS IDs by @ProfessorCha0s (thanks)
|
||||
* add Office LTSC 2024 KMS IDs by @MrRubberDucky (thanks)
|
128
arch.dockerfile
128
arch.dockerfile
@@ -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"]
|
27
compose.yaml
27
compose.yaml
@@ -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:
|
BIN
img/GUI.png
BIN
img/GUI.png
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
BIN
img/Office.png
Normal file
BIN
img/Office.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
img/Windows11ENTLTSC.png
Normal file
BIN
img/Windows11ENTLTSC.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
img/WindowsSRV2025.png
Normal file
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
BIN
img/webGUICustomIcon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
82
project.md
Normal file
82
project.md
Normal file
@@ -0,0 +1,82 @@
|
||||

|
||||
|
||||

|
||||
|
||||
${{ 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
@@ -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 \
|
||||
|
@@ -1,2 +0,0 @@
|
||||
#!/bin/ash
|
||||
netstat -an | grep -q ${KMS_PORT}
|
Reference in New Issue
Block a user